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 =>
|
||||
{
|
||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = "oidc";
|
||||
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddOpenIdConnect(options =>
|
||||
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme,options =>
|
||||
{
|
||||
options.Authority = Configuration["oidc:authority"];
|
||||
options.ClientId = Configuration["oidc:client_id"];
|
||||
options.ClientSecret = Configuration["oidc:client_secret"];
|
||||
|
||||
options.ResponseType = OpenIdConnectResponseType.Code;
|
||||
options.GetClaimsFromUserInfoEndpoint = true;
|
||||
options.GetClaimsFromUserInfoEndpoint = false;
|
||||
options.SaveTokens = true;
|
||||
options.UseTokenLifetime = true;
|
||||
options.Scope.Add("openid");
|
||||
options.Scope.Add("profile");
|
||||
options.Scope.Add("email");
|
||||
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
|
||||
options.Scope.Add(OpenIdConnectScope.OpenId);
|
||||
options.TokenValidationParameters = new
|
||||
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");
|
||||
}
|
||||
|
||||
var userInfo = await _oidcService.GetTokenDetails(token);
|
||||
if (userInfo == null)
|
||||
{
|
||||
return AuthenticateResult.Fail("Failed to get info for token");
|
||||
}
|
||||
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);
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
|
||||
@@ -45,14 +53,17 @@ namespace WebAPI.Auth
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
public static class OIDCClaimTypes
|
||||
{
|
||||
public static string Username => "Username";
|
||||
public static string Subject => "Subject";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
@@ -7,10 +9,5 @@ namespace WebAPI.Controllers
|
||||
{
|
||||
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.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using WebAPI.Auth;
|
||||
using WebAPI.Data;
|
||||
|
||||
namespace WebAPI.Controllers
|
||||
{
|
||||
[Authorize(Policy = "test")]
|
||||
[Authorize]
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class HelloWorldController : BaseController
|
||||
@@ -25,7 +26,14 @@ namespace WebAPI.Controllers
|
||||
[HttpGet]
|
||||
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 OIDCClientId { get; private set; }
|
||||
public static string OIDCClientSecret { get; set; }
|
||||
public static string OIDCUserInfoEndpoint { get; set; }
|
||||
public static void Init(IConfiguration configuration)
|
||||
{
|
||||
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.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using WebAPI.Data.Dto.OIDC;
|
||||
|
||||
namespace WebAPI.Data
|
||||
{
|
||||
@@ -49,6 +50,21 @@ namespace WebAPI.Data
|
||||
var responsecontent = await response.Content.ReadAsStringAsync();
|
||||
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.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using WebAPI.Data.Dto;
|
||||
|
||||
namespace WebAPI.Data
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace WebAPI.Data
|
||||
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
|
||||
{
|
||||
@@ -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
|
||||
{
|
||||
@@ -75,5 +76,19 @@ namespace WebAPI.Data
|
||||
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.AddScoped<PterodactylService>();
|
||||
services.AddScoped<OIDCService>();
|
||||
services.AddScoped<CustomAuthorizationFilter>();
|
||||
services.AddAuthentication(opt =>
|
||||
{
|
||||
opt.DefaultScheme = OIDCTokenAuthenticationDefaults.DefaultScheme;
|
||||
@@ -77,9 +76,6 @@ namespace WebAPI
|
||||
OIDCTokenAuthenticationDefaults.DefaultScheme,
|
||||
opt =>
|
||||
{
|
||||
opt.OIDCClientId = AppSettings.OIDCClientId;
|
||||
opt.OIDCClientSecret = AppSettings.OIDCClientSecret;
|
||||
opt.OIDCIntrospectionEndpoint = AppSettings.OIDCIntrospectionEndpoint;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"PterodactylAPIKey": "REPLACE_ME",
|
||||
"PterodactylPanelURL": "https://panel.orfl.xyz",
|
||||
"OIDCIntrospectionEndpoint": "authentik.mattstop.com/application/o/introspect/",
|
||||
"OIDCUserInfoEndpoint": "authentik.mattstop.com/application/o/userinfo/",
|
||||
"OIDCClientId": "",
|
||||
"OIDCClientSecret": ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user