Refactoring authentication to support server-side Blazor using a seamless login flow.
This commit is contained in:
parent
f3c823e667
commit
ce069ed45b
@ -4,11 +4,12 @@
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Providers
|
||||
@using Oqtane.Shared
|
||||
@inherits ModuleBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
@inject IServiceProvider ServiceProvider
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
@ -28,33 +29,62 @@
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<label class="form-check-label" for="Remember">Remember Me?</label>
|
||||
<input type="checkbox" class="form-check-input" name="Remember" @bind="@Remember" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="@Login">Login</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
<button type="button" class="btn btn-secondary" @onclick="@Cancel">Cancel</button>
|
||||
</div>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
|
||||
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; } = "";
|
||||
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; } = "";
|
||||
public bool Remember { get; set; } = false;
|
||||
|
||||
private async Task Login()
|
||||
private async Task Login()
|
||||
{
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.Password = Password;
|
||||
user.IsPersistent = Remember;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user.IsAuthenticated)
|
||||
{
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.Password = Password;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user != null)
|
||||
string ReturnUrl = PageState.QueryString["returnurl"];
|
||||
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
if (authstateprovider == null)
|
||||
{
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl("", true));
|
||||
// server-side Blazor
|
||||
var interop = new Interop(jsRuntime);
|
||||
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
|
||||
var fields = new { __RequestVerificationToken = antiforgerytoken, username = Username, password = Password, remember = Remember, returnurl = ReturnUrl };
|
||||
await interop.SubmitForm("/login/", fields);
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = "<div class=\"alert alert-danger\" role=\"alert\">User Does Not Exist</div>";
|
||||
// client-side Blazor
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl(ReturnUrl, true));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message = "<div class=\"alert alert-danger\" role=\"alert\">Login Failed. Please Remember That Passwords Are Case Sensitive.</div>";
|
||||
}
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
string ReturnUrl = PageState.QueryString["returnurl"];
|
||||
UriHelper.NavigateTo(NavigateUrl(ReturnUrl));
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,12 @@ using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class ServerAuthenticationStateProvider : AuthenticationStateProvider
|
||||
public class IdentityAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
//private readonly IUserService UserService;
|
||||
private readonly IUriHelper urihelper;
|
||||
|
||||
public ServerAuthenticationStateProvider(IUriHelper urihelper)
|
||||
public IdentityAuthenticationStateProvider(IUriHelper urihelper)
|
||||
{
|
||||
//this.UserService = UserService;
|
||||
this.urihelper = urihelper;
|
||||
}
|
||||
|
||||
@ -25,6 +23,7 @@ namespace Oqtane.Providers
|
||||
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();
|
@ -32,8 +32,7 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<Alias> GetAliasAsync(int AliasId)
|
||||
{
|
||||
List<Alias> aliases = await http.GetJsonAsync<List<Alias>>(apiurl);
|
||||
return aliases.Where(item => item.AliasId == AliasId).FirstOrDefault();
|
||||
return await http.GetJsonAsync<Alias>(apiurl + "/" + AliasId.ToString());
|
||||
}
|
||||
|
||||
public async Task AddAliasAsync(Alias alias)
|
||||
|
@ -8,6 +8,7 @@ namespace Oqtane.Services
|
||||
{
|
||||
Task<List<Module>> GetModulesAsync(int PageId);
|
||||
Task<List<Module>> GetModulesAsync(int SiteId, string ModuleDefinitionName);
|
||||
Task<Module> GetModuleAsync(int ModuleId);
|
||||
Task AddModuleAsync(Module module);
|
||||
Task UpdateModuleAsync(Module module);
|
||||
Task DeleteModuleAsync(int ModuleId);
|
||||
|
@ -7,6 +7,7 @@ namespace Oqtane.Services
|
||||
public interface IPageService
|
||||
{
|
||||
Task<List<Page>> GetPagesAsync(int SiteId);
|
||||
Task<Page> GetPageAsync(int PageId);
|
||||
Task AddPageAsync(Page page);
|
||||
Task UpdatePageAsync(Page page);
|
||||
Task DeletePageAsync(int PageId);
|
||||
|
@ -10,6 +10,8 @@ namespace Oqtane.Services
|
||||
|
||||
Task<User> GetUserAsync(int UserId);
|
||||
|
||||
Task<User> GetUserAsync(string Username);
|
||||
|
||||
Task AddUserAsync(User user);
|
||||
|
||||
Task UpdateUserAsync(User user);
|
||||
|
@ -39,6 +39,11 @@ namespace Oqtane.Services
|
||||
return modules.ToList();
|
||||
}
|
||||
|
||||
public async Task<Module> GetModuleAsync(int ModuleId)
|
||||
{
|
||||
return await http.GetJsonAsync<Module>(apiurl + "/" + ModuleId.ToString());
|
||||
}
|
||||
|
||||
public async Task AddModuleAsync(Module module)
|
||||
{
|
||||
await http.PostJsonAsync(apiurl, module);
|
||||
|
@ -30,6 +30,11 @@ namespace Oqtane.Services
|
||||
return pages.OrderBy(item => item.Order).ToList();
|
||||
}
|
||||
|
||||
public async Task<Page> GetPageAsync(int PageId)
|
||||
{
|
||||
return await http.GetJsonAsync<Page>(apiurl + "/" + PageId.ToString());
|
||||
}
|
||||
|
||||
public async Task AddPageAsync(Page page)
|
||||
{
|
||||
await http.PostJsonAsync(apiurl, page);
|
||||
|
@ -32,17 +32,7 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<Site> GetSiteAsync(int SiteId)
|
||||
{
|
||||
List<Site> sites = await http.GetJsonAsync<List<Site>>(apiurl);
|
||||
Site site;
|
||||
if (sites.Count == 1)
|
||||
{
|
||||
site = sites.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
site = sites.Where(item => item.SiteId == SiteId).FirstOrDefault();
|
||||
}
|
||||
return site;
|
||||
return await http.GetJsonAsync<Site>(apiurl + "/" + SiteId.ToString());
|
||||
}
|
||||
|
||||
public async Task AddSiteAsync(Site site)
|
||||
|
@ -35,8 +35,12 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<User> GetUserAsync(int UserId)
|
||||
{
|
||||
List<User> users = await http.GetJsonAsync<List<User>>(apiurl);
|
||||
return users.Where(item => item.UserId == UserId).FirstOrDefault();
|
||||
return await http.GetJsonAsync<User>(apiurl + "/" + UserId.ToString());
|
||||
}
|
||||
|
||||
public async Task<User> GetUserAsync(string Username)
|
||||
{
|
||||
return await http.GetJsonAsync<User>(apiurl + "/name/" + Username);
|
||||
}
|
||||
|
||||
public async Task AddUserAsync(User user)
|
||||
|
@ -13,17 +13,18 @@ namespace Oqtane.Shared
|
||||
this.jsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public Task<string> SetCookie(string name, string value, int days)
|
||||
public Task SetCookie(string name, string value, int days)
|
||||
{
|
||||
try
|
||||
{
|
||||
return jsRuntime.InvokeAsync<string>(
|
||||
jsRuntime.InvokeAsync<string>(
|
||||
"interop.setCookie",
|
||||
name, value, days);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,18 +42,48 @@ namespace Oqtane.Shared
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> AddCSS(string filename)
|
||||
public Task AddCSS(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsRuntime.InvokeAsync<string>(
|
||||
"interop.addCSS",
|
||||
filename);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> GetElementByName(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return jsRuntime.InvokeAsync<string>(
|
||||
"interop.addCSS",
|
||||
filename);
|
||||
"interop.getElementByName",
|
||||
name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public Task SubmitForm(string path, object fields)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsRuntime.InvokeAsync<string>(
|
||||
"interop.submitForm",
|
||||
path, fields);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ private async Task Refresh()
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = await UserService.GetCurrentUserAsync();
|
||||
user = await UserService.GetUserAsync(authState.User.Identity.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -25,7 +25,14 @@ namespace Oqtane.Shared
|
||||
string url = pagestate.Alias.Path + "/" + path;
|
||||
if (reload)
|
||||
{
|
||||
url += "?reload=true";
|
||||
if (url.Contains("?"))
|
||||
{
|
||||
url += "&reload=true";
|
||||
}
|
||||
else
|
||||
{
|
||||
url += "?reload=true";
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ namespace Oqtane.Client
|
||||
{
|
||||
// register auth services
|
||||
services.AddAuthorizationCore();
|
||||
services.AddScoped<ServerAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<ServerAuthenticationStateProvider>());
|
||||
services.AddScoped<IdentityAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<IdentityAuthenticationStateProvider>());
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<SiteState>();
|
||||
|
@ -1,10 +1,14 @@
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Providers
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Models
|
||||
@using Microsoft.JSInterop
|
||||
@inherits ThemeObjectBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IServiceProvider ServiceProvider
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
@ -22,13 +26,25 @@
|
||||
@code {
|
||||
private void LoginUser()
|
||||
{
|
||||
UriHelper.NavigateTo(NavigateUrl("login"));
|
||||
UriHelper.NavigateTo(NavigateUrl("login?returnurl=" + PageState.Page.Path));
|
||||
}
|
||||
|
||||
private async Task LogoutUser()
|
||||
{
|
||||
await UserService.LogoutUserAsync();
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl("", true));
|
||||
|
||||
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
|
||||
if (authstateprovider == null)
|
||||
{
|
||||
// server-side Blazor
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm("/logout/", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// client-side Blazor
|
||||
authstateprovider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl("login", true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,5 +29,23 @@ window.interop = {
|
||||
link.href = fileName;
|
||||
|
||||
head.appendChild(link);
|
||||
},
|
||||
submitForm: function (path, fields) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = path;
|
||||
|
||||
for (const key in fields) {
|
||||
if (fields.hasOwnProperty(key)) {
|
||||
const hiddenField = document.createElement('input');
|
||||
hiddenField.type = 'hidden';
|
||||
hiddenField.name = key;
|
||||
hiddenField.value = fields[key];
|
||||
form.appendChild(hiddenField);
|
||||
}
|
||||
}
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
};
|
||||
|
@ -4,6 +4,8 @@ using Oqtane.Repository;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -73,54 +75,48 @@ namespace Oqtane.Controllers
|
||||
users.DeleteUser(id);
|
||||
}
|
||||
|
||||
// GET api/<controller>/current
|
||||
[HttpGet("current")]
|
||||
public User Current()
|
||||
// GET api/<controller>/name/x
|
||||
[HttpGet("name/{name}")]
|
||||
public User GetByName(string name)
|
||||
{
|
||||
User user = null;
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = users.GetUser(User.Identity.Name);
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
return user;
|
||||
return users.GetUser(name);
|
||||
}
|
||||
|
||||
// POST api/<controller>/login
|
||||
[HttpPost("login")]
|
||||
public async Task<User> Login([FromBody] User user)
|
||||
{
|
||||
// TODO: seed host user - this logic should be moved to installation
|
||||
IdentityUser identityuser = await identityUserManager.FindByNameAsync("host");
|
||||
if (identityuser == null)
|
||||
{
|
||||
var result = await identityUserManager.CreateAsync(new IdentityUser { UserName = "host", Email = "host" }, "password");
|
||||
if (result.Succeeded)
|
||||
{
|
||||
users.AddUser(new Models.User { Username = "host", DisplayName = "host", IsSuperUser = true, Roles = "" });
|
||||
}
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// seed host user - this logic should be moved to installation
|
||||
IdentityUser identityuser = await identityUserManager.FindByNameAsync("host");
|
||||
if (identityuser == null)
|
||||
{
|
||||
var result = await identityUserManager.CreateAsync(new IdentityUser { UserName = "host", Email = "host" }, "password");
|
||||
if (result.Succeeded)
|
||||
{
|
||||
users.AddUser(new Models.User { Username = "host", DisplayName = "host", IsSuperUser = true, Roles = "" });
|
||||
}
|
||||
}
|
||||
|
||||
identityuser = await identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await identitySignInManager.SignInAsync(identityuser, false);
|
||||
await identitySignInManager.SignInAsync(identityuser, user.IsPersistent);
|
||||
user = users.GetUser(identityuser.UserName);
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
user = new Models.User { Username = user.Username, IsAuthenticated = false };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
user = new Models.User { Username = user.Username, IsAuthenticated = false };
|
||||
}
|
||||
}
|
||||
return user;
|
||||
|
3
Oqtane.Server/Pages/Login.cshtml
Normal file
3
Oqtane.Server/Pages/Login.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@page "/login"
|
||||
@namespace Oqtane.Pages
|
||||
@model Oqtane.Pages.LoginModel
|
52
Oqtane.Server/Pages/Login.cshtml.cs
Normal file
52
Oqtane.Server/Pages/Login.cshtml.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public class LoginModel : PageModel
|
||||
{
|
||||
|
||||
private readonly UserManager<IdentityUser> identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> identitySignInManager;
|
||||
|
||||
public LoginModel(UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager)
|
||||
{
|
||||
identityUserManager = IdentityUserManager;
|
||||
identitySignInManager = IdentitySignInManager;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnPostAsync(string username, string password, bool remember, string returnurl)
|
||||
{
|
||||
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
|
||||
|
||||
bool validuser = false;
|
||||
IdentityUser identityuser = await identityUserManager.FindByNameAsync(username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, password, false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
validuser = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (validuser)
|
||||
{
|
||||
var claims = new List<Claim>{ new Claim(ClaimTypes.Name, username) };
|
||||
var claimsIdentity = new ClaimsIdentity(claims, IdentityConstants.ApplicationScheme);
|
||||
var authProperties = new AuthenticationProperties{IsPersistent = remember};
|
||||
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);
|
||||
}
|
||||
|
||||
return LocalRedirect(Url.Content("~/" + returnurl));
|
||||
}
|
||||
}
|
||||
}
|
3
Oqtane.Server/Pages/Logout.cshtml
Normal file
3
Oqtane.Server/Pages/Logout.cshtml
Normal file
@ -0,0 +1,3 @@
|
||||
@page "/logout"
|
||||
@namespace Oqtane.Pages
|
||||
@model Oqtane.Pages.LogoutModel
|
26
Oqtane.Server/Pages/Logout.cshtml.cs
Normal file
26
Oqtane.Server/Pages/Logout.cshtml.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
[IgnoreAntiforgeryToken(Order = 1001)]
|
||||
[AllowAnonymous]
|
||||
public class LogoutModel : PageModel
|
||||
{
|
||||
public async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
|
||||
|
||||
return LocalRedirect(Url.Content("~/"));
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@
|
||||
<link href="css/site.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
@(Html.AntiForgeryToken())
|
||||
<app>@(await Html.RenderComponentAsync<App>())</app>
|
||||
|
||||
<script src="js/site.js"></script>
|
||||
|
@ -52,6 +52,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
/// determine if this module implements IModule
|
||||
Type moduletype = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(ModuleType))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IModule)))
|
||||
.FirstOrDefault();
|
||||
|
@ -24,7 +24,7 @@ namespace Oqtane.Repository
|
||||
aliasname = accessor.HttpContext.Request.Host.Value;
|
||||
string path = accessor.HttpContext.Request.Path.Value;
|
||||
string[] segments = path.Split('/');
|
||||
if (segments[1] != "~")
|
||||
if (segments[0] == "api" && segments[1] != "~")
|
||||
{
|
||||
aliasname += "/" + segments[1];
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
/// determine if this theme implements ITheme
|
||||
Type themeType = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(Namespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
|
||||
if (themeType != null)
|
||||
@ -105,7 +106,10 @@ namespace Oqtane.Repository
|
||||
theme.ThemeControls += (themeControlType.FullName + ", " + typename[1] + ";");
|
||||
}
|
||||
// containers
|
||||
Type[] containertypes = assembly.GetTypes().Where(item => item.Namespace.StartsWith(Namespace)).Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||
Type[] containertypes = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(Namespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||
foreach (Type containertype in containertypes)
|
||||
{
|
||||
theme.ContainerControls += (containertype.FullName + ", " + typename[1] + ";");
|
||||
|
@ -17,12 +17,9 @@ using System.Runtime.Loader;
|
||||
using Oqtane.Services;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Client;
|
||||
using Oqtane.Shared;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Oqtane.Providers;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
namespace Oqtane.Server
|
||||
{
|
||||
@ -47,25 +44,27 @@ namespace Oqtane.Server
|
||||
services.AddRazorPages();
|
||||
services.AddServerSideBlazor();
|
||||
|
||||
// server-side Blazor does not register HttpClient by default
|
||||
// setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
|
||||
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
|
||||
{
|
||||
// setup HttpClient for server side in a client side compatible fashion
|
||||
services.AddScoped<HttpClient>(s =>
|
||||
{
|
||||
// creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
|
||||
var uriHelper = s.GetRequiredService<IUriHelper>();
|
||||
return new HttpClient
|
||||
var httpContextAccessor = s.GetRequiredService<IHttpContextAccessor>();
|
||||
var authToken = httpContextAccessor.HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];
|
||||
var client = new HttpClient(new HttpClientHandler { UseCookies = false });
|
||||
if (authToken != null)
|
||||
{
|
||||
BaseAddress = new Uri(uriHelper.GetBaseUri())
|
||||
};
|
||||
client.DefaultRequestHeaders.Add("Cookie", ".AspNetCore.Identity.Application=" + authToken);
|
||||
}
|
||||
client.BaseAddress = new Uri(uriHelper.GetBaseUri());
|
||||
return client;
|
||||
});
|
||||
}
|
||||
|
||||
// register auth services
|
||||
// register auth services
|
||||
services.AddAuthorizationCore();
|
||||
services.AddScoped<ServerAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<ServerAuthenticationStateProvider>());
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<SiteState>();
|
||||
|
@ -20,6 +20,14 @@ window.interop = {
|
||||
}
|
||||
return "";
|
||||
},
|
||||
getElementByName: function (name) {
|
||||
var elements = document.getElementsByName(name);
|
||||
if (elements.length) {
|
||||
return elements[0].value;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
addCSS: function (fileName) {
|
||||
var head = document.head;
|
||||
var link = document.createElement("link");
|
||||
@ -29,5 +37,23 @@ window.interop = {
|
||||
link.href = fileName;
|
||||
|
||||
head.appendChild(link);
|
||||
},
|
||||
submitForm: function (path, fields) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = path;
|
||||
|
||||
for (const key in fields) {
|
||||
if (fields.hasOwnProperty(key)) {
|
||||
const hiddenField = document.createElement('input');
|
||||
hiddenField.type = 'hidden';
|
||||
hiddenField.name = key;
|
||||
hiddenField.value = fields[key];
|
||||
form.appendChild(hiddenField);
|
||||
}
|
||||
}
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
};
|
||||
|
@ -14,5 +14,7 @@ namespace Oqtane.Models
|
||||
public string Password { get; set; }
|
||||
[NotMapped]
|
||||
public bool IsAuthenticated { get; set; }
|
||||
[NotMapped]
|
||||
public bool IsPersistent { get; set; }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user