diff --git a/Web/Pages/CreateServer.razor b/Web/Pages/CreateServer.razor
index 1a3029e..a7c81a3 100644
--- a/Web/Pages/CreateServer.razor
+++ b/Web/Pages/CreateServer.razor
@@ -7,7 +7,7 @@
@code {
- protected async override Task OnInitializedAsync()
+ protected override async Task OnInitializedAsync()
{
base.OnInitializedAsync();
}
diff --git a/WebAPI/Auth/CustomAuthorizationFilter.cs b/WebAPI/Auth/CustomAuthorizationFilter.cs
new file mode 100644
index 0000000..698e510
--- /dev/null
+++ b/WebAPI/Auth/CustomAuthorizationFilter.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Linq;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
+using WebAPI.Data;
+
+namespace WebAPI.Auth
+{
+ public class OIDCAuthorization : ServiceFilterAttribute
+ {
+ public OIDCAuthorization() : base(typeof(CustomAuthorizationFilter))
+ {
+
+ }
+ }
+ ///
+ /// Authorization filter for checking the bearer token against the OIDC service.
+ /// Will short circuit on an invalid token
+ ///
+ ///
+ public class CustomAuthorizationFilter: IAsyncAuthorizationFilter
+ {
+ private readonly ILogger _logger;
+ private readonly OIDCService _oidcService;
+ private readonly AppDbContext _appDbContext;
+
+ public CustomAuthorizationFilter(ILogger logger, OIDCService oidcService, AppDbContext appDbContext)
+ {
+ _logger = logger;
+ _oidcService = oidcService;
+ _appDbContext = appDbContext;
+ }
+
+ public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
+ {
+ try
+ {
+ var httpContext = context.HttpContext;
+ var bearerToken = httpContext?.Request?.Headers[HeaderNames.Authorization].FirstOrDefault();
+ if (string.IsNullOrEmpty(bearerToken))
+ {
+ throw new Exception("Need a token");
+ }
+
+ string token = bearerToken.Split(" ").ElementAt(1);
+ if (!await _oidcService.ValidateAccessToken(token))
+ {
+ throw new Exception("bad token");
+ }
+
+ }
+ catch (Exception e)
+ {
+ context.Result = new ForbidResult(e.Message);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/WebAPI/Auth/OIDCAuthHandler.cs b/WebAPI/Auth/OIDCAuthHandler.cs
new file mode 100644
index 0000000..53021aa
--- /dev/null
+++ b/WebAPI/Auth/OIDCAuthHandler.cs
@@ -0,0 +1,58 @@
+using System.Linq;
+using System.Security.Claims;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Net.Http.Headers;
+using WebAPI.Data;
+
+namespace WebAPI.Auth
+{
+ public class OIDCTokenAuthenticationHandler : AuthenticationHandler
+ {
+ private readonly OIDCService _oidcService;
+ public OIDCTokenAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, OIDCService oidcService) : base(options, logger, encoder, clock)
+ {
+ _oidcService = oidcService;
+ }
+
+ protected override async Task HandleAuthenticateAsync()
+ {
+ var bearerToken = Request.Headers[HeaderNames.Authorization].FirstOrDefault() ?? "";
+ if (string.IsNullOrEmpty(bearerToken))
+ {
+ return AuthenticateResult.Fail("no token");
+ }
+
+ var token = bearerToken.Split(" ").ElementAt(1);
+ if (!await _oidcService.ValidateAccessToken(token))
+ {
+ return AuthenticateResult.Fail("failed to validate token");
+ }
+
+ var identity = new ClaimsIdentity(new[]
+ {
+ new Claim(ClaimTypes.Authentication, token)
+ }, Scheme.Name);
+ var principal = new ClaimsPrincipal(identity);
+ return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
+ }
+ }
+
+ public class OIDCTokenAuthenticationOptions : AuthenticationSchemeOptions
+ {
+ public string OIDCIntrospectionEndpoint { get; set; }
+ public string OIDCClientId { get; set; }
+ public string OIDCClientSecret { get; set; }
+
+ }
+
+ public class OIDCTokenAuthenticationDefaults
+ {
+ public static string DefaultScheme => "OIDCAuthentication";
+ }
+}
\ No newline at end of file
diff --git a/WebAPI/Controllers/HelloWorldController.cs b/WebAPI/Controllers/HelloWorldController.cs
index e0b434c..ea37667 100644
--- a/WebAPI/Controllers/HelloWorldController.cs
+++ b/WebAPI/Controllers/HelloWorldController.cs
@@ -2,33 +2,30 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using WebAPI.Auth;
using WebAPI.Data;
namespace WebAPI.Controllers
{
+ [Authorize(Policy = "test")]
[Route("api/[controller]")]
[ApiController]
public class HelloWorldController : BaseController
{
private readonly PterodactylService _pterodactylService;
- private readonly OIDCService _oidcService;
- public HelloWorldController(PterodactylService pterodactylService, OIDCService oidcService)
+ public HelloWorldController(PterodactylService pterodactylService)
{
_pterodactylService = pterodactylService;
- _oidcService = oidcService;
}
[HttpGet]
public async Task HelloWorld()
{
- if (await _oidcService.ValidateAccessToken(BearerToken))
- {
- return "Validated";
- }
- return "Failed";
+ return "Success";
}
}
diff --git a/WebAPI/Data/AppSettings.cs b/WebAPI/Data/AppSettings.cs
index 4711cf1..f8ef49e 100644
--- a/WebAPI/Data/AppSettings.cs
+++ b/WebAPI/Data/AppSettings.cs
@@ -7,7 +7,9 @@ namespace WebAPI.Data
{
public static string PterodactylAPIKey { get; private set; }
public static string PterodactylPanelURL { get; private set; }
- public static string OIDCUserInfoEndpoint { get; private set; }
+ public static string OIDCIntrospectionEndpoint { get; private set; }
+ public static string OIDCClientId { get; private set; }
+ public static string OIDCClientSecret { get; set; }
public static void Init(IConfiguration configuration)
{
var fields = typeof(AppSettings).GetProperties();
diff --git a/WebAPI/Data/OIDCService.cs b/WebAPI/Data/OIDCService.cs
index 2ed297f..de8a665 100644
--- a/WebAPI/Data/OIDCService.cs
+++ b/WebAPI/Data/OIDCService.cs
@@ -1,14 +1,21 @@
using System;
+using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
namespace WebAPI.Data
{
public class OIDCService
{
+ protected class IntrospectionResponse
+ {
+ public bool Active { get; set; }
+ }
private HttpClient _httpClient { get; set; }
private ILogger _logger { get; set; }
public OIDCService(ILogger logger)
@@ -24,15 +31,23 @@ namespace WebAPI.Data
/// success
public async Task ValidateAccessToken(string accessToken)
{
- Uri requestUri = new Uri($"{AppSettings.OIDCUserInfoEndpoint}");
- HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri);
- request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
+ Uri requestUri = new Uri($"https://{AppSettings.OIDCIntrospectionEndpoint}");
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);
+ request.Content = new FormUrlEncodedContent(new Dictionary()
+ {
+ {"token", accessToken}
+ });
+ string encodedAuth = Convert.ToBase64String(Encoding.GetEncoding(Encoding.Latin1.CodePage)
+ .GetBytes($"{AppSettings.OIDCClientId}:{AppSettings.OIDCClientSecret}"));
+ request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth);
HttpResponseMessage response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
return false;
}
- return true;
+
+ var responsecontent = await response.Content.ReadAsStringAsync();
+ return JsonConvert.DeserializeObject(responsecontent).Active;
}
}
diff --git a/WebAPI/Startup.cs b/WebAPI/Startup.cs
index 28bf8ed..37c54ac 100644
--- a/WebAPI/Startup.cs
+++ b/WebAPI/Startup.cs
@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Security.Claims;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
@@ -12,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
+using WebAPI.Auth;
using WebAPI.Data;
namespace WebAPI
@@ -61,8 +66,21 @@ namespace WebAPI
});
});
services.AddDbContext(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
- services.AddSingleton();
- services.AddSingleton();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddAuthentication(opt =>
+ {
+ opt.DefaultScheme = OIDCTokenAuthenticationDefaults.DefaultScheme;
+ })
+ .AddScheme(
+ OIDCTokenAuthenticationDefaults.DefaultScheme,
+ opt =>
+ {
+ opt.OIDCClientId = AppSettings.OIDCClientId;
+ opt.OIDCClientSecret = AppSettings.OIDCClientSecret;
+ opt.OIDCIntrospectionEndpoint = AppSettings.OIDCIntrospectionEndpoint;
+ });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -79,6 +97,7 @@ namespace WebAPI
app.UseRouting();
+ app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
diff --git a/WebAPI/appsettings.Development.json b/WebAPI/appsettings.Development.json
index 2900c0a..182c7fb 100644
--- a/WebAPI/appsettings.Development.json
+++ b/WebAPI/appsettings.Development.json
@@ -4,8 +4,8 @@
},
"Logging": {
"LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
+ "Default": "Debug",
+ "Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
diff --git a/WebAPI/appsettings.json b/WebAPI/appsettings.json
index 01fe3bc..36832eb 100644
--- a/WebAPI/appsettings.json
+++ b/WebAPI/appsettings.json
@@ -12,5 +12,7 @@
"AllowedHosts": "*",
"PterodactylAPIKey": "REPLACE_ME",
"PterodactylPanelURL": "https://panel.orfl.xyz",
- "OIDCUserInfoEndpoint": "https://authentik.mattstop.com/application/o/userinfo/"
+ "OIDCIntrospectionEndpoint": "authentik.mattstop.com/application/o/introspect/",
+ "OIDCClientId": "",
+ "OIDCClientSecret": ""
}