Compare commits

...

9 Commits

11 changed files with 177 additions and 8 deletions

1
.gitignore vendored
View File

@ -412,3 +412,4 @@ FodyWeavers.xsd
# Built Visual Studio Code Extensions
*.vsix
/src/DigitalData.Auth.API/Secrets

View File

@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C0123B52-5168-4C87-98A0-11A220EC392F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Auth.API", "src\DigitalData.Auth.API\DigitalData.Auth.API.csproj", "{1AF05BC2-6F15-420A-85F6-E6F8740CD557}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Auth.API", "src\DigitalData.Auth.API\DigitalData.Auth.API.csproj", "{1AF05BC2-6F15-420A-85F6-E6F8740CD557}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -24,4 +24,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{1AF05BC2-6F15-420A-85F6-E6F8740CD557} = {C0123B52-5168-4C87-98A0-11A220EC392F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D163037-043C-41AE-AB94-C7314F2C38DA}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,32 @@
namespace DigitalData.Auth.API.Config
{
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()
{
HttpOnly = true,
SameSite = SameSiteMode.Strict
};
public string CookieName { get; init; } = "AuthToken";
public required string Issuer { get; init; }
public bool RequireHttpsMetadata { get; init; } = true;
}
}

View File

@ -0,0 +1,25 @@
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;
}
}
}

View File

@ -0,0 +1,17 @@
namespace DigitalData.Auth.API.Config
{
public class Consumer
{
public required string Route { get; init; }
public required string Audience { get; init; }
private CookieOptionsProvider? _cookieOptions;
public CookieOptionsProvider CookieOptions { get => _cookieOptions ?? Parent?.DefaultCookieOptions; init => _cookieOptions = value; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public AuthApiParams Parent { private get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}
}

View File

@ -0,0 +1,73 @@
namespace DigitalData.Auth.API.Config
{
public class CookieOptionsProvider
{
public TimeSpan Lifetime { get; init; } = new(1, 0, 0);
private readonly CookieOptions _optionsBase = new();
#region CookieOptions
//
// Summary:
// Gets or sets the domain to associate the cookie with.
//
// Returns:
// The domain to associate the cookie with.
public string? Domain { get => _optionsBase.Domain; set => _optionsBase.Domain = value; }
//
// Summary:
// Gets or sets the cookie path.
//
// Returns:
// The cookie path.
public string? Path { get => _optionsBase.Path; set => _optionsBase.Path = value; }
//
// Summary:
// Gets or sets the expiration date and time for the cookie.
//
// Returns:
// The expiration date and time for the cookie.
public DateTimeOffset? Expires { get => _optionsBase.Expires; set => _optionsBase.Expires = value; }
//
// Summary:
// Gets or sets a value that indicates whether to transmit the cookie using Secure
// Sockets Layer (SSL)--that is, over HTTPS only.
//
// Returns:
// true to transmit the cookie only over an SSL connection (HTTPS); otherwise, false.
public bool Secure { get => _optionsBase.Secure; set => _optionsBase.Secure = value; }
//
// Summary:
// Gets or sets the value for the SameSite attribute of the cookie. The default
// value is Microsoft.AspNetCore.Http.SameSiteMode.Unspecified
//
// Returns:
// The Microsoft.AspNetCore.Http.SameSiteMode representing the enforcement mode
// of the cookie.
public SameSiteMode SameSite { get => _optionsBase.SameSite; set => _optionsBase.SameSite = value; }
//
// Summary:
// Gets or sets a value that indicates whether a cookie is inaccessible by client-side
// script.
//
// Returns:
// true if a cookie must not be accessible by client-side script; otherwise, false.
public bool HttpOnly { get => _optionsBase.HttpOnly; set => _optionsBase.HttpOnly = value; }
//
// Summary:
// Gets or sets the max-age for the cookie.
//
// Returns:
// The max-age date and time for the cookie.
public TimeSpan? MaxAge { get => _optionsBase.MaxAge; set => _optionsBase.MaxAge = value; }
//
// Summary:
// Indicates if this cookie is essential for the application to function correctly.
// If true then consent policy checks may be bypassed. The default value is false.
public bool IsEssential { get => _optionsBase.IsEssential; set => _optionsBase.IsEssential = value; }
#endregion
public CookieOptions Create(TimeSpan? lifetime = null) => new(_optionsBase) { Expires = DateTime.UtcNow.AddTicks(lifetime?.Ticks ?? Lifetime.Ticks) };
}
}

View File

@ -1,6 +0,0 @@
@DigitalData.Auth.API_HostAddress = http://localhost:5075
GET {{DigitalData.Auth.API_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,4 @@
namespace DigitalData.Auth.API.Dto
{
public record ConsumerApi(string Name, string Password);
}

View File

@ -0,0 +1,4 @@
namespace DigitalData.Auth.API.Dto
{
public record ConsumerApiLogin(string Name, string Password);
}

View File

@ -1,5 +1,9 @@
var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;
builder.Configuration.AddJsonFile("consumers.json", true, true);
// Add services to the container.
builder.Services.AddControllers();
@ -22,4 +26,4 @@ app.UseAuthorization();
app.MapControllers();
app.Run();
app.Run();

View File

@ -0,0 +1,12 @@
{
"Consumers": [
{
"Name": "WorkFlow.API",
"Password": "t3B|aiJ'i-snLzNRj3B{9=&:lM5P@'i<>L"
},
{
"Name": "DigitalData.UserManager.API",
"Password": "a098Hvu1-y29ep{KPQO]#>8TK+fk{O`_d"
}
]
}