Added userinfo endpoint usage and api now properly creates necessary claims to start doing database stuff
This commit is contained in:
@@ -36,22 +36,21 @@ namespace Web
|
|||||||
services.AddAuthentication(options =>
|
services.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = "oidc";
|
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
||||||
})
|
})
|
||||||
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
|
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
.AddOpenIdConnect(options =>
|
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme,options =>
|
||||||
{
|
{
|
||||||
options.Authority = Configuration["oidc:authority"];
|
options.Authority = Configuration["oidc:authority"];
|
||||||
options.ClientId = Configuration["oidc:client_id"];
|
options.ClientId = Configuration["oidc:client_id"];
|
||||||
options.ClientSecret = Configuration["oidc:client_secret"];
|
options.ClientSecret = Configuration["oidc:client_secret"];
|
||||||
|
|
||||||
options.ResponseType = OpenIdConnectResponseType.Code;
|
options.ResponseType = OpenIdConnectResponseType.Code;
|
||||||
options.GetClaimsFromUserInfoEndpoint = true;
|
options.GetClaimsFromUserInfoEndpoint = false;
|
||||||
options.SaveTokens = true;
|
options.SaveTokens = true;
|
||||||
options.UseTokenLifetime = true;
|
options.UseTokenLifetime = true;
|
||||||
options.Scope.Add("openid");
|
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
|
||||||
options.Scope.Add("profile");
|
options.Scope.Add(OpenIdConnectScope.OpenId);
|
||||||
options.Scope.Add("email");
|
|
||||||
options.TokenValidationParameters = new
|
options.TokenValidationParameters = new
|
||||||
TokenValidationParameters
|
TokenValidationParameters
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
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))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Authorization filter for checking the bearer token against the OIDC service.
|
|
||||||
/// Will short circuit on an invalid token
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class CustomAuthorizationFilter: IAsyncAuthorizationFilter
|
|
||||||
{
|
|
||||||
private readonly ILogger<CustomAuthorizationFilter> _logger;
|
|
||||||
private readonly OIDCService _oidcService;
|
|
||||||
private readonly AppDbContext _appDbContext;
|
|
||||||
|
|
||||||
public CustomAuthorizationFilter(ILogger<CustomAuthorizationFilter> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,9 +34,17 @@ namespace WebAPI.Auth
|
|||||||
return AuthenticateResult.Fail("failed to validate token");
|
return AuthenticateResult.Fail("failed to validate token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userInfo = await _oidcService.GetTokenDetails(token);
|
||||||
|
if (userInfo == null)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Fail("Failed to get info for token");
|
||||||
|
}
|
||||||
var identity = new ClaimsIdentity(new[]
|
var identity = new ClaimsIdentity(new[]
|
||||||
{
|
{
|
||||||
new Claim(ClaimTypes.Authentication, token)
|
new Claim(ClaimTypes.Authentication, token),
|
||||||
|
new Claim(ClaimTypes.Name, userInfo.Name),
|
||||||
|
new Claim(OIDCClaimTypes.Username, userInfo.PreferredUsername),
|
||||||
|
new Claim(OIDCClaimTypes.Subject, userInfo.Sub)
|
||||||
}, Scheme.Name);
|
}, Scheme.Name);
|
||||||
var principal = new ClaimsPrincipal(identity);
|
var principal = new ClaimsPrincipal(identity);
|
||||||
return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
|
return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
|
||||||
@@ -45,9 +53,6 @@ namespace WebAPI.Auth
|
|||||||
|
|
||||||
public class OIDCTokenAuthenticationOptions : AuthenticationSchemeOptions
|
public class OIDCTokenAuthenticationOptions : AuthenticationSchemeOptions
|
||||||
{
|
{
|
||||||
public string OIDCIntrospectionEndpoint { get; set; }
|
|
||||||
public string OIDCClientId { get; set; }
|
|
||||||
public string OIDCClientSecret { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,4 +60,10 @@ namespace WebAPI.Auth
|
|||||||
{
|
{
|
||||||
public static string DefaultScheme => "OIDCAuthentication";
|
public static string DefaultScheme => "OIDCAuthentication";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class OIDCClaimTypes
|
||||||
|
{
|
||||||
|
public static string Username => "Username";
|
||||||
|
public static string Subject => "Subject";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
@@ -7,10 +9,5 @@ namespace WebAPI.Controllers
|
|||||||
{
|
{
|
||||||
public class BaseController : ControllerBase
|
public class BaseController : ControllerBase
|
||||||
{
|
{
|
||||||
protected string BearerToken =>
|
|
||||||
Request.Headers.Keys.Contains(HeaderNames.Authorization) &&
|
|
||||||
Request.Headers[HeaderNames.Authorization].Count > 0
|
|
||||||
? Request.Headers[HeaderNames.Authorization].First().Split(" ")[1]
|
|
||||||
: String.Empty;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,13 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using WebAPI.Auth;
|
using WebAPI.Auth;
|
||||||
using WebAPI.Data;
|
using WebAPI.Data;
|
||||||
|
|
||||||
namespace WebAPI.Controllers
|
namespace WebAPI.Controllers
|
||||||
{
|
{
|
||||||
[Authorize(Policy = "test")]
|
[Authorize]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class HelloWorldController : BaseController
|
public class HelloWorldController : BaseController
|
||||||
@@ -25,7 +26,14 @@ namespace WebAPI.Controllers
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<string> HelloWorld()
|
public async Task<string> HelloWorld()
|
||||||
{
|
{
|
||||||
return "Success";
|
return JsonConvert.SerializeObject(User.Claims.Select(claim => new {claim.Type, claim.Value}));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("NameRequired")]
|
||||||
|
public string NameRequired()
|
||||||
|
{
|
||||||
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace WebAPI.Data
|
|||||||
public static string OIDCIntrospectionEndpoint { get; private set; }
|
public static string OIDCIntrospectionEndpoint { get; private set; }
|
||||||
public static string OIDCClientId { get; private set; }
|
public static string OIDCClientId { get; private set; }
|
||||||
public static string OIDCClientSecret { get; set; }
|
public static string OIDCClientSecret { get; set; }
|
||||||
|
public static string OIDCUserInfoEndpoint { get; set; }
|
||||||
public static void Init(IConfiguration configuration)
|
public static void Init(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var fields = typeof(AppSettings).GetProperties();
|
var fields = typeof(AppSettings).GetProperties();
|
||||||
|
|||||||
34
WebAPI/Data/Dto/OIDC/OIDCUserInfoResponse.cs
Normal file
34
WebAPI/Data/Dto/OIDC/OIDCUserInfoResponse.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace WebAPI.Data.Dto.OIDC
|
||||||
|
{
|
||||||
|
public class OIDCUserInfoResponse
|
||||||
|
{
|
||||||
|
[JsonProperty("email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("email_verified")]
|
||||||
|
public bool EmailVerified { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("given_name")]
|
||||||
|
public string GivenName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("family_name")]
|
||||||
|
public string FamilyName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("preferred_username")]
|
||||||
|
public string PreferredUsername { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("nickname")]
|
||||||
|
public string Nickname { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("groups")]
|
||||||
|
public string[] Groups { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("sub")]
|
||||||
|
public string Sub { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
16
WebAPI/Data/Dto/PterodactylCreateUserRequest.cs
Normal file
16
WebAPI/Data/Dto/PterodactylCreateUserRequest.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace WebAPI.Data.Dto
|
||||||
|
{
|
||||||
|
public class PterodactylCreateUserRequest
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
[JsonProperty("external_id")]
|
||||||
|
public string ExternalId { get; set; }
|
||||||
|
[JsonProperty("first_name")]
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
[JsonProperty("last_name")]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
WebAPI/Data/Dto/PterodactylCreateUserResponse.cs
Normal file
11
WebAPI/Data/Dto/PterodactylCreateUserResponse.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace WebAPI.Data.Dto
|
||||||
|
{
|
||||||
|
public class PterodactylCreateUserResponseAttributes
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
public class PterodactylCreateUserResponse
|
||||||
|
{
|
||||||
|
public PterodactylCreateUserResponseAttributes Attributes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using WebAPI.Data.Dto.OIDC;
|
||||||
|
|
||||||
namespace WebAPI.Data
|
namespace WebAPI.Data
|
||||||
{
|
{
|
||||||
@@ -50,5 +51,20 @@ namespace WebAPI.Data
|
|||||||
return JsonConvert.DeserializeObject<IntrospectionResponse>(responsecontent).Active;
|
return JsonConvert.DeserializeObject<IntrospectionResponse>(responsecontent).Active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<OIDCUserInfoResponse> GetTokenDetails(string accessToken)
|
||||||
|
{
|
||||||
|
HttpRequestMessage requestMessage =
|
||||||
|
new HttpRequestMessage(HttpMethod.Get, $"https://{AppSettings.OIDCUserInfoEndpoint}");
|
||||||
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||||
|
HttpResponseMessage response = await _httpClient.SendAsync(requestMessage);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var responsecontent = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<OIDCUserInfoResponse>(responsecontent);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using WebAPI.Data.Dto;
|
||||||
|
|
||||||
namespace WebAPI.Data
|
namespace WebAPI.Data
|
||||||
{
|
{
|
||||||
@@ -46,7 +47,7 @@ namespace WebAPI.Data
|
|||||||
return JsonConvert.DeserializeObject<T>(responsedata);
|
return JsonConvert.DeserializeObject<T>(responsedata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T> SendGet<T>(string endpoint, IEnumerable includeParameters, bool client=false)
|
private async Task<T> SendGet<T>(string endpoint, IEnumerable includeParameters, bool client=false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -61,7 +62,7 @@ namespace WebAPI.Data
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T> SendPost<T>(string endpoint, object obj, IEnumerable includeParameters, bool client = false)
|
private async Task<T> SendPost<T>(string endpoint, object obj, IEnumerable includeParameters, bool client = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -75,5 +76,19 @@ namespace WebAPI.Data
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PterodactylCreateUserResponse> SendPterodactylUserCreate(string username, string email,
|
||||||
|
string firstname, string lastname, string externalId)
|
||||||
|
{
|
||||||
|
var requestObj = new PterodactylCreateUserRequest()
|
||||||
|
{
|
||||||
|
Email = email,
|
||||||
|
ExternalId = externalId,
|
||||||
|
FirstName = firstname,
|
||||||
|
LastName = lastname,
|
||||||
|
Username = username
|
||||||
|
};
|
||||||
|
return await SendPost<PterodactylCreateUserResponse>("users", requestObj, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,6 @@ namespace WebAPI
|
|||||||
services.AddDbContext<AppDbContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
|
services.AddDbContext<AppDbContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
|
||||||
services.AddScoped<PterodactylService>();
|
services.AddScoped<PterodactylService>();
|
||||||
services.AddScoped<OIDCService>();
|
services.AddScoped<OIDCService>();
|
||||||
services.AddScoped<CustomAuthorizationFilter>();
|
|
||||||
services.AddAuthentication(opt =>
|
services.AddAuthentication(opt =>
|
||||||
{
|
{
|
||||||
opt.DefaultScheme = OIDCTokenAuthenticationDefaults.DefaultScheme;
|
opt.DefaultScheme = OIDCTokenAuthenticationDefaults.DefaultScheme;
|
||||||
@@ -77,9 +76,6 @@ namespace WebAPI
|
|||||||
OIDCTokenAuthenticationDefaults.DefaultScheme,
|
OIDCTokenAuthenticationDefaults.DefaultScheme,
|
||||||
opt =>
|
opt =>
|
||||||
{
|
{
|
||||||
opt.OIDCClientId = AppSettings.OIDCClientId;
|
|
||||||
opt.OIDCClientSecret = AppSettings.OIDCClientSecret;
|
|
||||||
opt.OIDCIntrospectionEndpoint = AppSettings.OIDCIntrospectionEndpoint;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"PterodactylAPIKey": "REPLACE_ME",
|
"PterodactylAPIKey": "REPLACE_ME",
|
||||||
"PterodactylPanelURL": "https://panel.orfl.xyz",
|
"PterodactylPanelURL": "https://panel.orfl.xyz",
|
||||||
"OIDCIntrospectionEndpoint": "authentik.mattstop.com/application/o/introspect/",
|
"OIDCIntrospectionEndpoint": "authentik.mattstop.com/application/o/introspect/",
|
||||||
|
"OIDCUserInfoEndpoint": "authentik.mattstop.com/application/o/userinfo/",
|
||||||
"OIDCClientId": "",
|
"OIDCClientId": "",
|
||||||
"OIDCClientSecret": ""
|
"OIDCClientSecret": ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user