refactor(ConsumerService): Entfernt ReadLocalAsync Methode.
- LocalConsumer Eigenschaft in AuthApiParams hinzugefügt.
This commit is contained in:
parent
f77a68be8d
commit
efae188d5c
@ -4,46 +4,18 @@ namespace DigitalData.Auth.API.Config
|
|||||||
{
|
{
|
||||||
public class AuthApiParams
|
public class AuthApiParams
|
||||||
{
|
{
|
||||||
private IEnumerable<Consumer> _consumers = new List<Consumer>();
|
|
||||||
|
|
||||||
public IEnumerable<Consumer> Consumers
|
|
||||||
{
|
|
||||||
get => _consumers;
|
|
||||||
init
|
|
||||||
{
|
|
||||||
_consumers = value;
|
|
||||||
for (int i = 0; i < _consumers.Count(); i++)
|
|
||||||
_consumers.ElementAt(i).Parent = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Consumer DefaultConsumer => Consumers.First();
|
|
||||||
|
|
||||||
public CookieOptionsProvider DefaultCookieOptions { get; init; } = new()
|
public CookieOptionsProvider DefaultCookieOptions { get; init; } = new()
|
||||||
{
|
{
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
SameSite = SameSiteMode.Strict
|
SameSite = SameSiteMode.Strict
|
||||||
};
|
};
|
||||||
|
|
||||||
public string CookieName { get; init; } = "AuthToken";
|
public string DefaultCookieName { get; init; } = "AuthToken";
|
||||||
|
|
||||||
public required string Issuer { get; init; }
|
public required string Issuer { get; init; }
|
||||||
|
|
||||||
public bool RequireHttpsMetadata { get; init; } = true;
|
public bool RequireHttpsMetadata { get; init; } = true;
|
||||||
|
|
||||||
public class Consumer
|
public required Consumer LocalConsumer { get; init; }
|
||||||
{
|
|
||||||
public required string Route { get; init; }
|
|
||||||
|
|
||||||
public required string Audience { get; init; }
|
|
||||||
|
|
||||||
private CookieOptionsProvider? _cookieOptions;
|
|
||||||
|
|
||||||
#pragma warning disable CS8603 // Possible null reference return.
|
|
||||||
public CookieOptionsProvider CookieOptions { get => _cookieOptions ?? Parent?.DefaultCookieOptions; init => _cookieOptions = value; }
|
|
||||||
#pragma warning restore CS8603 // Possible null reference return.
|
|
||||||
|
|
||||||
internal AuthApiParams? Parent { private get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,27 +0,0 @@
|
|||||||
using static DigitalData.Auth.API.Config.AuthApiParams;
|
|
||||||
|
|
||||||
namespace DigitalData.Auth.API.Config
|
|
||||||
{
|
|
||||||
public static class ConfigExtensions
|
|
||||||
{
|
|
||||||
public static Consumer? GetByAudience(this IEnumerable<Consumer> audiances, string name) => audiances.FirstOrDefault(a => a.Audience == name);
|
|
||||||
|
|
||||||
public static Consumer? GetByRoute(this IEnumerable<Consumer> audiances, string route) => audiances.FirstOrDefault(a => a.Route == route);
|
|
||||||
|
|
||||||
public static bool TryGetByAudience(this IEnumerable<Consumer> audiances, string audience, out Consumer audiance)
|
|
||||||
{
|
|
||||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
|
||||||
audiance = audiances.GetByAudience(audience);
|
|
||||||
#pragma warning restore CS8601 // Possible null reference assignment.
|
|
||||||
return audiance is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetByRoute(this IEnumerable<Consumer> audiances, string route, out Consumer audiance)
|
|
||||||
{
|
|
||||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
|
||||||
audiance = audiances.SingleOrDefault(a => a.Route == route);
|
|
||||||
#pragma warning restore CS8601 // Possible null reference assignment.
|
|
||||||
return audiance is not null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
using DigitalData.Core.Abstractions.Security;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace DigitalData.Auth.API.Config
|
|
||||||
{
|
|
||||||
public static class DependentExtensions
|
|
||||||
{
|
|
||||||
private static AuthApiParams? _authApiParams;
|
|
||||||
|
|
||||||
private static AuthApiParams AuthApiParams
|
|
||||||
{
|
|
||||||
get => _authApiParams
|
|
||||||
?? throw new InvalidOperationException(
|
|
||||||
$"DependentExtensions have not been added to the application or are not configured correctly. {typeof(AuthApiParams)} cannot be provided."
|
|
||||||
);
|
|
||||||
set => _authApiParams = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IApplicationBuilder AddDependentExtensions(this IApplicationBuilder application)
|
|
||||||
{
|
|
||||||
var authApiParamOptions = application.ApplicationServices.GetRequiredService<IOptions<AuthApiParams>>();
|
|
||||||
_authApiParams = authApiParamOptions.Value;
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryGetByRoute(this IEnumerable<IAsymmetricTokenDescriptor> descriptors, string consumerRoute, out IAsymmetricTokenDescriptor descriptor)
|
|
||||||
{
|
|
||||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
|
||||||
descriptor = null;
|
|
||||||
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
|
|
||||||
if (!AuthApiParams.Consumers.TryGetByRoute(consumerRoute, out var consumer)
|
|
||||||
|| !descriptors.TryGet(AuthApiParams.Issuer, consumer.Audience, out var _descriptor))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
descriptor = _descriptor;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -45,7 +45,7 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
_consumerSignatureHandler = apiSignatureHandler;
|
_consumerSignatureHandler = apiSignatureHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> CreateTokenAsync(LogInDto login, string consumerRoute, bool cookie = true)
|
private async Task<IActionResult> CreateTokenAsync(LogInDto login, string consumerName, bool cookie = true)
|
||||||
{
|
{
|
||||||
bool isValid = await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password);
|
bool isValid = await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password);
|
||||||
|
|
||||||
@ -53,8 +53,12 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
//find the user
|
//find the user
|
||||||
var uRes = await _userService.ReadByUsernameAsync(login.Username);
|
var uRes = await _userService.ReadByUsernameAsync(login.Username);
|
||||||
if (uRes.IsFailed || !_apiParams.Consumers.TryGetByRoute(consumerRoute, out var consumer))
|
if (uRes.IsFailed)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var consumer = await _consumerService.ReadByNameAsync(consumerName);
|
||||||
|
if (consumer is null)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor))
|
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor))
|
||||||
@ -65,7 +69,8 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
//set cookie
|
//set cookie
|
||||||
if (cookie)
|
if (cookie)
|
||||||
{
|
{
|
||||||
Response.Cookies.Append(_apiParams.CookieName, token, consumer.CookieOptions.Create(lifetime: descriptor.Lifetime));
|
var cookieOptions = consumer.CookieOptions ?? _apiParams.DefaultCookieOptions;
|
||||||
|
Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime));
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -74,20 +79,20 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
|
|
||||||
private async Task<IActionResult> CreateTokenAsync(ConsumerLogin login, bool cookie = true)
|
private async Task<IActionResult> CreateTokenAsync(ConsumerLogin login, bool cookie = true)
|
||||||
{
|
{
|
||||||
var api = await _consumerService.ReadByNameAsync(login.Name);
|
var consumer = await _consumerService.ReadByNameAsync(login.Name);
|
||||||
|
if (consumer is null || consumer.Password != login.Password)
|
||||||
if (api is null || api.Password != login.Password)
|
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, _apiParams.DefaultConsumer.Audience, out var descriptor))
|
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, _apiParams.LocalConsumer.Audience, out var descriptor))
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||||
|
|
||||||
var token = _consumerSignatureHandler.WriteToken(api, descriptor);
|
var token = _consumerSignatureHandler.WriteToken(consumer, descriptor);
|
||||||
|
|
||||||
//set cookie
|
//set cookie
|
||||||
if (cookie)
|
if (cookie)
|
||||||
{
|
{
|
||||||
Response.Cookies.Append(_apiParams.CookieName, token, _apiParams.DefaultConsumer.CookieOptions.Create(lifetime: descriptor.Lifetime));
|
var cookieOptions = _apiParams.LocalConsumer.CookieOptions ?? _apiParams.DefaultCookieOptions;
|
||||||
|
Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime));
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -95,13 +100,13 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Add role depends on group name
|
//TODO: Add role depends on group name
|
||||||
[HttpPost("{consumerRoute}/login")]
|
[HttpPost("{consumerName}/login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> Login([FromForm] LogInDto login, [FromRoute] string consumerRoute)
|
public async Task<IActionResult> Login([FromForm] LogInDto login, [FromRoute] string consumerName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await CreateTokenAsync(login, consumerRoute, true);
|
return await CreateTokenAsync(login, consumerName, true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -130,7 +135,7 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Response.Cookies.Delete(_apiParams.CookieName);
|
Response.Cookies.Delete(_apiParams.DefaultCookieName);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -140,12 +145,12 @@ namespace DigitalData.Auth.API.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{consumerRoute}")]
|
[HttpPost("{consumerName}")]
|
||||||
public async Task<IActionResult> CreateTokenViaBody([FromBody] LogInDto login, [FromRoute] string consumerRoute, [FromQuery] bool cookie = false)
|
public async Task<IActionResult> CreateTokenViaBody([FromBody] LogInDto login, [FromRoute] string consumerName, [FromQuery] bool cookie = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await CreateTokenAsync(login, consumerRoute, cookie);
|
return await CreateTokenAsync(login, consumerName, cookie);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -68,6 +68,6 @@
|
|||||||
public bool IsEssential { get => _optionsBase.IsEssential; set => _optionsBase.IsEssential = value; }
|
public bool IsEssential { get => _optionsBase.IsEssential; set => _optionsBase.IsEssential = value; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public CookieOptions Create() => new(_optionsBase) { Expires = DateTime.UtcNow.AddTicks(Lifetime.Ticks) };
|
public CookieOptions Create(TimeSpan? lifetime = null) => new(_optionsBase) { Expires = DateTime.UtcNow.AddTicks(lifetime?.Ticks ?? Lifetime.Ticks) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,13 +85,13 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|||||||
{
|
{
|
||||||
options.RequireHttpsMetadata = apiParams!.RequireHttpsMetadata;
|
options.RequireHttpsMetadata = apiParams!.RequireHttpsMetadata;
|
||||||
options.ClaimsIssuer = apiParams!.Issuer;
|
options.ClaimsIssuer = apiParams!.Issuer;
|
||||||
options.Audience = apiParams!.DefaultConsumer.Audience;
|
options.Audience = apiParams.LocalConsumer.Audience;
|
||||||
options.TokenValidationParameters = new()
|
options.TokenValidationParameters = new()
|
||||||
{
|
{
|
||||||
ValidateIssuer = true,
|
ValidateIssuer = true,
|
||||||
ValidIssuer = apiParams!.Issuer,
|
ValidIssuer = apiParams!.Issuer,
|
||||||
ValidateAudience = true,
|
ValidateAudience = true,
|
||||||
ValidAudience = apiParams!.DefaultConsumer.Audience,
|
ValidAudience = apiParams.LocalConsumer.Audience,
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
IssuerSigningKey = issuerSigningKeyInitiator?.Value
|
IssuerSigningKey = issuerSigningKeyInitiator?.Value
|
||||||
};
|
};
|
||||||
@ -102,7 +102,7 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|||||||
{
|
{
|
||||||
// if there is no token read related cookie
|
// if there is no token read related cookie
|
||||||
if (context.Token is null // if there is no token
|
if (context.Token is null // if there is no token
|
||||||
&& context.Request.Cookies.TryGetValue(apiParams!.CookieName, out var token) // get token from cookies
|
&& context.Request.Cookies.TryGetValue(apiParams!.DefaultCookieName, out var token) // get token from cookies
|
||||||
&& token is not null)
|
&& token is not null)
|
||||||
context.Token = token;
|
context.Token = token;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -112,12 +112,10 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.AddDependentExtensions();
|
|
||||||
|
|
||||||
issuerSigningKeyInitiator = new Lazy<SecurityKey>(() =>
|
issuerSigningKeyInitiator = new Lazy<SecurityKey>(() =>
|
||||||
{
|
{
|
||||||
var factory = app.Services.GetRequiredService<ICryptoFactory>();
|
var factory = app.Services.GetRequiredService<ICryptoFactory>();
|
||||||
var desc = factory.TokenDescriptors.Get(apiParams.Issuer, apiParams.DefaultConsumer.Audience);
|
var desc = factory.TokenDescriptors.Get(apiParams.Issuer, apiParams.LocalConsumer.Audience);
|
||||||
return desc.Validator.SecurityKey;
|
return desc.Validator.SecurityKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,20 +9,15 @@ namespace DigitalData.Auth.API.Services
|
|||||||
{
|
{
|
||||||
private readonly IEnumerable<Consumer> _consumers;
|
private readonly IEnumerable<Consumer> _consumers;
|
||||||
|
|
||||||
private readonly AuthApiParams _authApiParams;
|
public ConfiguredConsumerService(IOptions<IEnumerable<Consumer>> consumeroptions)
|
||||||
|
|
||||||
public ConfiguredConsumerService(IOptions<IEnumerable<Consumer>> consumeroptions, IOptions<AuthApiParams> authApiParamOptions)
|
|
||||||
{
|
{
|
||||||
_consumers = consumeroptions.Value;
|
_consumers = consumeroptions.Value;
|
||||||
_authApiParams = authApiParamOptions.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Consumer?> ReadByIdAsync(int id) => Task.Run(() => _consumers.FirstOrDefault(api => api.Id == id));
|
public Task<Consumer?> ReadByIdAsync(int id) => Task.Run(() => _consumers.FirstOrDefault(api => api.Id == id));
|
||||||
|
|
||||||
public Task<Consumer?> ReadByNameAsync(string name) => Task.Run(() => _consumers.FirstOrDefault(api => api.Name == name));
|
public Task<Consumer?> ReadByNameAsync(string name) => Task.Run(() => _consumers.FirstOrDefault(api => api.Name == name));
|
||||||
|
|
||||||
public Task<Consumer> ReadLocalAsync() => Task.Run(() => _consumers.FirstOrDefault() ?? throw new InvalidOperationException("Unable to read the local consumer because no consumers are available."));
|
|
||||||
|
|
||||||
public async Task<bool> VerifyAsync(string name, string password) => (await ReadByNameAsync(name))?.Password == password;
|
public async Task<bool> VerifyAsync(string name, string password) => (await ReadByNameAsync(name))?.Password == password;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,8 +8,6 @@ namespace DigitalData.Auth.API.Services.Contracts
|
|||||||
|
|
||||||
public Task<Consumer?> ReadByNameAsync(string name);
|
public Task<Consumer?> ReadByNameAsync(string name);
|
||||||
|
|
||||||
public Task<Consumer> ReadLocalAsync();
|
|
||||||
|
|
||||||
public Task<bool> VerifyAsync(string name, string password);
|
public Task<bool> VerifyAsync(string name, string password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -14,17 +14,13 @@
|
|||||||
"ServerName": "DD-VMP01-DC01",
|
"ServerName": "DD-VMP01-DC01",
|
||||||
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works"
|
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works"
|
||||||
},
|
},
|
||||||
"Consumers": [
|
|
||||||
{
|
|
||||||
"Route": "api",
|
|
||||||
"Audience": "api.digitaldata.works"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Route": "work-flow",
|
|
||||||
"Audience": "work-flow.digitaldata.works"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Issuer": "auth.digitaldata.works",
|
"Issuer": "auth.digitaldata.works",
|
||||||
|
"LocalConsumer": {
|
||||||
|
"Id": -1,
|
||||||
|
"Name": "auth-flow",
|
||||||
|
"Audience": "auth.digitaldata.works",
|
||||||
|
"Password": "n7l^)s,v;jbr0c+x%urk=fak4[s==z?<"
|
||||||
|
},
|
||||||
"CryptParams": {
|
"CryptParams": {
|
||||||
"KeySizeInBits": 4096,
|
"KeySizeInBits": 4096,
|
||||||
"Padding": "OaepSHA512",
|
"Padding": "OaepSHA512",
|
||||||
@ -38,7 +34,7 @@
|
|||||||
{
|
{
|
||||||
"Id": "4062504f-f081-43d1-b4ed-78256a0879e1",
|
"Id": "4062504f-f081-43d1-b4ed-78256a0879e1",
|
||||||
"Issuer": "auth.digitaldata.works",
|
"Issuer": "auth.digitaldata.works",
|
||||||
"Audience": "api.digitaldata.works",
|
"Audience": "auth.digitaldata.works",
|
||||||
"IsEncrypted": true,
|
"IsEncrypted": true,
|
||||||
"Lifetime": "5:00:00"
|
"Lifetime": "5:00:00"
|
||||||
},
|
},
|
||||||
@ -48,6 +44,13 @@
|
|||||||
"Audience": "work-flow.digitaldata.works",
|
"Audience": "work-flow.digitaldata.works",
|
||||||
"IsEncrypted": true,
|
"IsEncrypted": true,
|
||||||
"Lifetime": "02:00:00"
|
"Lifetime": "02:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "9e3b0e68-c3e4-489e-b68c-47df26d6b612",
|
||||||
|
"Issuer": "auth.digitaldata.works",
|
||||||
|
"Audience": "user-manager.digitaldata.works",
|
||||||
|
"IsEncrypted": true,
|
||||||
|
"Lifetime": "02:00:00"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,12 @@
|
|||||||
"Consumers": [
|
"Consumers": [
|
||||||
{
|
{
|
||||||
"Id": 0,
|
"Id": 0,
|
||||||
"Name": "auth-flow",
|
|
||||||
"Audience": "auth.digitaldata.works",
|
|
||||||
"Password": "aQ9z!2@TgY7b#fHcD3pLmV1$wX"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"Name": "work-flow",
|
"Name": "work-flow",
|
||||||
"Audience": "work-flow.digitaldata.works",
|
"Audience": "work-flow.digitaldata.works",
|
||||||
"Password": "t3B|aiJ'i-snLzNRj3B{9=&:lM5P@'iL"
|
"Password": "t3B|aiJ'i-snLzNRj3B{9=&:lM5P@'iL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Id": 2,
|
"Id": 1,
|
||||||
"Name": "user-manager",
|
"Name": "user-manager",
|
||||||
"Audience": "user-manager.digitaldata.works",
|
"Audience": "user-manager.digitaldata.works",
|
||||||
"Password": "a098Hvu1-y29ep{KPQO]#>8TK+fk{O`_d"
|
"Password": "a098Hvu1-y29ep{KPQO]#>8TK+fk{O`_d"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user