create separate API methods for tokens (short-lived) and personal access tokens (long-lived), include global antiforgery filter to mitigate XSRF when using cookie auth (ignored when using Jwt)
This commit is contained in:
parent
c616878a64
commit
f6b3874668
|
@ -575,7 +575,7 @@ else
|
|||
|
||||
private async Task CreateToken()
|
||||
{
|
||||
_token = await UserService.GetTokenAsync();
|
||||
_token = await UserService.GetPersonalAccessTokenAsync();
|
||||
}
|
||||
|
||||
private void ToggleClientSecret()
|
||||
|
|
|
@ -109,5 +109,11 @@ namespace Oqtane.Services
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> GetTokenAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Get personal access token for current user (administrators only)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> GetPersonalAccessTokenAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,5 +84,10 @@ namespace Oqtane.Services
|
|||
{
|
||||
return await GetStringAsync($"{Apiurl}/token");
|
||||
}
|
||||
|
||||
public async Task<string> GetPersonalAccessTokenAsync()
|
||||
{
|
||||
return await GetStringAsync($"{Apiurl}/personalaccesstoken");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -522,8 +522,23 @@ namespace Oqtane.Controllers
|
|||
|
||||
// GET api/<controller>/token
|
||||
[HttpGet("token")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public string Token()
|
||||
{
|
||||
var token = "";
|
||||
var sitesettings = HttpContext.GetSiteSettings();
|
||||
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
|
||||
if (!string.IsNullOrEmpty(secret))
|
||||
{
|
||||
token = _jwtManager.GenerateToken(_tenantManager.GetAlias(), (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Audience", "20")));
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
// GET api/<controller>/personalaccesstoken
|
||||
[HttpGet("personalaccesstoken")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public string PersonalAccessToken()
|
||||
{
|
||||
var token = "";
|
||||
var sitesettings = HttpContext.GetSiteSettings();
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.AddSingleton<IDatabaseManager, DatabaseManager>();
|
||||
services.AddSingleton<IConfigManager, ConfigManager>();
|
||||
services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
|
||||
services.AddSingleton<AutoValidateAntiforgeryTokenFilter>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Oqtane.Security
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class AutoValidateAntiforgeryTokenAttribute : Attribute, IFilterFactory, IOrderedFilter
|
||||
{
|
||||
public int Order { get; set; } = 1000;
|
||||
|
||||
public bool IsReusable => true;
|
||||
|
||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||
{
|
||||
return serviceProvider.GetRequiredService<AutoValidateAntiforgeryTokenFilter>();
|
||||
}
|
||||
}
|
||||
}
|
64
Oqtane.Server/Security/AutoValidateAntiforgeryTokenFilter.cs
Normal file
64
Oqtane.Server/Security/AutoValidateAntiforgeryTokenFilter.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace Oqtane.Security
|
||||
{
|
||||
public class AutoValidateAntiforgeryTokenFilter : IAsyncAuthorizationFilter, IAntiforgeryPolicy
|
||||
{
|
||||
private readonly IAntiforgery _antiforgery;
|
||||
|
||||
public AutoValidateAntiforgeryTokenFilter(IAntiforgery antiforgery)
|
||||
{
|
||||
_antiforgery = antiforgery;
|
||||
}
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (!context.IsEffectivePolicy<IAntiforgeryPolicy>(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldValidate(context))
|
||||
{
|
||||
try
|
||||
{
|
||||
await _antiforgery.ValidateRequestAsync(context.HttpContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
context.Result = new AntiforgeryValidationFailedResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldValidate(AuthorizationFilterContext context)
|
||||
{
|
||||
// ignore antiforgery validation if a bearer token was provided
|
||||
if (context.HttpContext.Request.Headers.ContainsKey("Authorization"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore antiforgery validation for GET, HEAD, TRACE, OPTIONS
|
||||
var method = context.HttpContext.Request.Method;
|
||||
if (HttpMethods.IsGet(method) || HttpMethods.IsHead(method) || HttpMethods.IsTrace(method) || HttpMethods.IsOptions(method))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// everything else requires antiforgery validation (ie. POST, PUT, DELETE)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -128,10 +128,13 @@ namespace Oqtane
|
|||
|
||||
services.AddOqtaneAuthorizationPolicies();
|
||||
|
||||
services.AddMvc()
|
||||
.AddNewtonsoftJson()
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.ConfigureOqtaneMvc(); // any additional configuration from IStartup classes
|
||||
services.AddMvc(options =>
|
||||
{
|
||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
})
|
||||
.AddNewtonsoftJson()
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.ConfigureOqtaneMvc(); // any additional configuration from IStartup classes
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user