Compare commits

..

25 Commits

Author SHA1 Message Date
bdf68b6bc4 Bump version to 1.0.2
Updated project version from 1.0.1 to 1.0.2 in the `.csproj` file.
This includes changes to `<Version>`, `<AssemblyVersion>`, and
`<FileVersion>` to reflect a minor update or patch.
2026-05-29 10:24:12 +02:00
c69635fc43 Refactor and enhance CookieNamesTests methods
Refactored and renamed several test methods in `CookieNamesTests` to improve clarity and align with updated functionality. Replaced methods testing `GetEnvelopeReceiverKeyOrDefault` with new methods for `GetEnvelopeReceiverCookieName` and `GetEnvelopeKeyOrDefault`. Added new tests to validate behavior with default cookie names and incorrect bases. Renamed `TryGetEnvelopeReceiverKey` methods to `TryGetEnvelopeKey` for consistency, while maintaining their functionality.
2026-05-29 10:21:50 +02:00
98fbbea5c3 Refactor cookie name methods and update tests
Renamed and refactored methods in `CookieNames` to improve naming consistency and simplify usage. Added overloads with default `defaultCookieName` set to `"AuthToken"`. Updated XML documentation to reflect these changes.

Updated `CookieNamesTests` to use the new method names and signatures. Adjusted test cases to align with the new default behavior and ensure compatibility.

These changes enhance code readability, reduce redundancy, and standardize method naming conventions.
2026-05-29 10:21:27 +02:00
f455241af1 Add unit tests for CookieNames class functionality
Added a new `CookieNamesTests` class to validate methods in the
`CookieNames` class, including scenarios for generating and
extracting cookie names and keys. Tests cover valid, invalid,
and unrelated cookie names, as well as round-trip validation
of `GetEnvelopeReceiverCookieName` and `GetEnvelopeReceiverKeyOrDefault`.

Updated `DigitalData.Auth.Tests.csproj` to include a project
reference to `DigitalData.Auth.Claims` for testing purposes.
2026-05-29 09:50:24 +02:00
2551de233f Refactor cookie handling and bump version to 1.0.1
Refactored `CookieNames.cs` to replace the `GetEnvelopeReceiverCookieName(string key)` method with new methods for extracting envelope receiver keys from cookie names:
- Added `GetEnvelopeReceiverKeyOrDefault` to extract keys or return `null` if the format is invalid.
- Added `TryGetEnvelopeReceiverKey` to attempt key extraction with a success flag.

Updated `DigitalData.Auth.Claims.csproj` to increment `Version`, `AssemblyVersion`, and `FileVersion` from 1.0.0 to 1.0.1, reflecting the new functionality.
2026-05-29 09:50:10 +02:00
25387238e3 Bump version to 1.4.1 in project file
Updated `DigitalData.Auth.API.csproj` to increment `<Version>`, `<AssemblyVersion>`, and `<FileVersion>` from `1.4.0` to `1.4.1`. This minor version update reflects bug fixes or small improvements without introducing breaking changes.
2026-05-29 09:12:47 +02:00
2c78ed106c Add CookieNames helper for constructing cookie names
Introduce a new static class `CookieNames` in the `DigitalData.Auth.Claims` namespace to centralize and standardize the construction of cookie names for envelope receiver tokens.

- Added `GetEnvelopeReceiverCookieName` methods to generate cookie names with or without a default cookie name.
- Updated `AuthController` to use the `CookieNames` helper for constructing cookie names, replacing direct concatenation logic.
- Improved maintainability and consistency of cookie naming conventions across the application.
2026-05-29 09:01:21 +02:00
8f722ce3c9 Add DigitalData.Auth.Claims project and enhance JWT handling
Added the `DigitalData.Auth.Claims` project to the solution,
including its build and debug configurations. Updated
`DigitalData.Auth.API.csproj` to reference the new project
and incremented version numbers to 1.4.0.

Enhanced `Program.cs` with a new JWT signature handler for
`EnvelopeReceiverSecretDto`, generating claims for envelope
and receiver-specific data. Added `DirectorySearchService`
to the service collection, configured via `DirectorySearchOptions`.
2026-05-29 08:48:10 +02:00
2fed1baff5 Add JWT token support for EnvelopeReceiver entities
Enhanced the `AuthController` to support JWT token generation
for `EnvelopeReceiverSecretDto` entities. Added a new
dependency `IJwtSignatureHandler<EnvelopeReceiverSecretDto>`
and updated the constructor to inject it.

Refactored the `CreateTokenForEnvelopeReceiver` method:
- Added a `cookie` query parameter to control token delivery.
- Implemented consumer validation using `_consumerService`.
- Added token descriptor retrieval from `_keyPool`.
- Improved `AccessCode` validation for `EnvelopeReceiver`.
- Added logic to set tokens as cookies or return in the body.

Updated `using` directives to include required namespaces.
Improved error handling, readability, and flexibility in
token generation and delivery.
2026-05-29 08:47:54 +02:00
3ba55cbe9a Add DigitalData.Auth.Claims project and EnvelopeClaimNames
Added a new .NET project `DigitalData.Auth.Claims` targeting `net8.0`
with metadata for NuGet packaging. The project is configured to
not generate a NuGet package on build.

Introduced the `EnvelopeClaimNames` static class to define strongly-typed
constants for custom JWT claim names specific to envelope receiver tokens.
These include `EnvelopeId`, `EnvelopeUuid`, `ReceiverId`, and
`ReceiverSignature`. Added XML documentation for all constants.
2026-05-29 08:47:18 +02:00
90d74282d8 Add envelope receiver token endpoint to AuthController
Refactor `AuthController` to include `IMediator` dependency and
introduce a new `CreateTokenForEnvelopeReceiver` API endpoint
to handle envelope receiver authentication.

- Updated `using` directives to remove unused namespaces and
  add required ones for new functionality.
- Added `ReceiverLogin` model to represent envelope receiver
  login credentials.
- Implemented `ReadEnvelopeReceiverSecretQuery` to validate
  access codes for envelope receivers.
- Cleaned up unused fields and dependencies in `AuthController`.
2026-05-29 00:21:36 +02:00
a7a386924e Update JwtBearer and EnvelopeGenerator packages
Added Microsoft.AspNetCore.Authentication.JwtBearer (8.0.17) to the test project.
Updated EnvelopeGenerator to version 1.2.0.3 in the API project.
Aligned JwtBearer version to 8.0.17 for the net8.0 target framework.
2026-05-29 00:21:10 +02:00
e55a6a3663 Add global exception handling middleware
Introduced `ExceptionHandlingMiddleware` to handle exceptions
globally, log errors, and return appropriate JSON responses.
Registered the middleware in `Program.cs` to ensure all requests
are processed through it. Added localization support in
`Program.cs` to enable localized error messages and other
features.
2026-05-29 00:20:55 +02:00
e80ad19c96 Add MediatR package to enable Mediator design pattern
The `MediatR` package (version 12.5.0) was added to the
`DigitalData.Auth.API.csproj` file. This introduces support
for the Mediator design pattern, which helps reduce coupling
between components and facilitates structured communication
via requests, commands, or queries.
2026-05-28 22:37:29 +02:00
d2d992b41e Add DbTriggerParams section to appsettings.json
Introduced a new `DbTriggerParams` configuration section in
`appsettings.json` to define database triggers for various
application components.

The section includes keys such as `Envelope`, `History`,
`EmailOut`, `EnvelopeReceiverReadOnly`, `Receiver`, and
`EmailTemplate`, each with corresponding arrays of trigger
names. This change enhances the application's ability to
manage database events in a structured and configurable way.
2026-05-28 22:30:28 +02:00
ff8d683040 Add EF Core SQL Server support to the project
Added `Microsoft.EntityFrameworkCore.SqlServer` package to the
`DigitalData.Auth.API.csproj` file to enable SQL Server as the
database provider. Updated `Program.cs` to include the
`Microsoft.EntityFrameworkCore` namespace for database interaction.
2026-05-28 22:26:48 +02:00
163916e330 Update dependencies and integrate EnvelopeGenerator
Updated `DigitalData.Auth.Tests.csproj` to include `Microsoft.EntityFrameworkCore` version `8.0.17`.

Refactored `AuthController.cs` to remove unused imports and update to the latest `DigitalData.Core.Abstraction.Application` namespaces.

Streamlined `DigitalData.Auth.API.csproj`:
- Changed target framework to `net8.0`.
- Upgraded `DigitalData.Core.Abstractions` to `4.3.0` and `DigitalData.Core.Application` to `3.4.0`.
- Added `EnvelopeGenerator` and `Microsoft.EntityFrameworkCore` dependencies.
- Removed outdated `UserManager` dependencies.

Enhanced `Program.cs`:
- Integrated `EnvelopeGenerator` services with database context and caching configuration.
- Removed `AddUserManager` service registration.
- Added SQL Server logging and error handling for `DbContext`.

These changes improve maintainability, adopt modern frameworks, and introduce new functionality with `EnvelopeGenerator`.
2026-05-28 22:26:31 +02:00
6bcc16cead Update project version to 1.3.0
Bump <Version>, <AssemblyVersion>, and <FileVersion> in DigitalData.Auth.API.csproj from 1.2.0 to 1.3.0.
2026-02-03 14:00:46 +01:00
a461cbaa71 Add CommonUserRoles section to appsettings.json
Added "CommonUserRoles" array with roles: Admin, UserManager, ContentManager, and Signatory to appsettings.json for improved role management configuration. No other changes made.
2026-02-03 13:58:45 +01:00
ce69779c9f Enhance JWT user claims with configurable roles
Updated Program.cs to include "CommonUserRoles" from configuration in JWT claims for users, adding them under ClaimTypes.Role if present. Also added System.Security.Claims using directive to support this change. This enables dynamic role assignment in user tokens.
2026-02-03 13:58:32 +01:00
31a371ecb9 Update consumer name for Id 2 in repository config
Changed the "Name" field for consumer Id 2 from "sign-flow-gen" to "sign-flow" in consumer-repository.json. No other fields were affected.
2026-02-03 13:01:50 +01:00
e530bceacd Update Audience for sign-flow-gen in consumer-repository
Changed the "Audience" field for the consumer with Id 2 ("sign-flow-gen") from "sign-flow-gen.digitaldata.works" to "sign-flow.digitaldata.works" in consumer-repository.json. No other fields were affected.
2026-02-02 12:56:06 +01:00
c82c7baed7 Update JWT Audience value in appsettings.json
Changed the Audience for JWT config with Id "f3c0881b-c349-442a-ac24-d02da0798abd" from "sign-flow-gen.digitaldata.works" to "sign-flow.digitaldata.works" to reflect the correct audience for token validation.
2026-02-02 12:55:56 +01:00
fec5a21612 Restrict NLog setup to non-dev; set log level to Trace
NLog provider setup and clearing of logging providers now occur
only outside development environments. The minimum logging level
is explicitly set to Trace for all environments.
2026-02-02 12:55:41 +01:00
09cb93736d chore: Aktualisierung des Entwicklungsportals zur Verwendung von 9090 2025-07-22 17:51:52 +02:00
14 changed files with 607 additions and 49 deletions

View File

@@ -0,0 +1,66 @@
namespace DigitalData.Auth.Claims
{
/// <summary>
/// Provides helpers for building cookie names used in the DigitalData.Auth ecosystem.
/// </summary>
public static class CookieNames
{
private const string ReceiverSuffix = "SignFLOWReceiver.";
/// <summary>
/// Builds the cookie name for an envelope receiver token.
/// </summary>
/// <param name="defaultCookieName">The base cookie name configured in <c>AuthApiParams</c>.</param>
/// <param name="key">The unique envelope receiver key.</param>
/// <returns>A cookie name in the format <c>{defaultCookieName}SignFLOWReceiver.{key}</c>.</returns>
public static string GetEnvelopeReceiverCookieName(string defaultCookieName, string key)
=> defaultCookieName + ReceiverSuffix + key;
/// <summary>
/// Builds the cookie name for an envelope receiver token.
/// </summary>
/// <param name="key">The unique envelope receiver key.</param>
/// <returns>A cookie name in the format <c>{defaultCookieName}SignFLOWReceiver.{key}</c>.</returns>
public static string GetEnvelopeReceiverCookieName(string key)
=> "AuthToken" + ReceiverSuffix + key;
/// <summary>
/// Extracts the envelope receiver key from a cookie name, or returns <see langword="null"/> if the cookie name does not match the expected format.
/// </summary>
/// <param name="cookieName">The full cookie name in the format <c>{defaultCookieName}SignFLOWReceiver.{key}</c>.</param>
/// <param name="defaultCookieName">The base cookie name configured in <c>AuthApiParams</c>.</param>
/// <returns>The envelope receiver key, or <see langword="null"/> if the cookie name does not match the expected format.</returns>
public static string? GetEnvelopeKeyOrDefault(string cookieName, string defaultCookieName = "AuthToken")
{
var prefix = defaultCookieName + ReceiverSuffix;
return cookieName.StartsWith(prefix, StringComparison.Ordinal)
? cookieName[prefix.Length..]
: null;
}
/// <summary>
/// Tries to extract the envelope key from a cookie name.
/// </summary>
/// <param name="cookieName">The full cookie name in the format <c>{defaultCookieName}SignFLOWReceiver.{key}</c>.</param>
/// <param name="defaultCookieName">The base cookie name configured in <c>AuthApiParams</c>.</param>
/// <param name="key">The extracted envelope key if the cookie name matches the expected format; otherwise <see langword="null"/>.</param>
/// <returns><see langword="true"/> if the key was successfully extracted; otherwise <see langword="false"/>.</returns>
public static bool TryGetEnvelopeKey(string cookieName, string defaultCookieName, out string? key)
{
key = GetEnvelopeKeyOrDefault(cookieName, defaultCookieName);
return key is not null;
}
/// <summary>
/// Tries to extract the envelope key from a cookie name.
/// </summary>
/// <param name="cookieName">The full cookie name in the format <c>{defaultCookieName}SignFLOWReceiver.{key}</c>.</param>
/// <param name="key">The extracted envelope key if the cookie name matches the expected format; otherwise <see langword="null"/>.</param>
/// <returns><see langword="true"/> if the key was successfully extracted; otherwise <see langword="false"/>.</returns>
public static bool TryGetEnvelopeKey(string cookieName, out string? key)
{
key = GetEnvelopeKeyOrDefault(cookieName, "AuthToken");
return key is not null;
}
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- NuGet package metadata -->
<PackageId>DigitalData.Auth.Claims</PackageId>
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>DigitalData.Auth.Claims</Product>
<Description>
Provides strongly-typed JWT claim name constants for the DigitalData.Auth ecosystem.
Includes domain-specific claim definitions (e.g. envelope and receiver claims)
to be shared across API, client, and consumer projects.
</Description>
<Copyright>Copyright 2026 Digital Data GmbH</Copyright>
<RepositoryUrl>https://git.dd/AppStd/DigitalData.Auth</RepositoryUrl>
<PackageTags>digital data auth claims jwt constants</PackageTags>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Version>1.0.2</Version>
<AssemblyVersion>1.0.2</AssemblyVersion>
<FileVersion>1.0.2</FileVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,34 @@
namespace DigitalData.Auth.Claims
{
/// <summary>
/// Defines the custom JWT claim names used for envelope receiver tokens.
/// These claims are domain-specific and have no equivalent in <see cref="Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames"/>.
/// </summary>
public static class EnvelopeClaimNames
{
/// <summary>
/// The database primary key of the envelope.
/// Claim name: <c>envelope_id</c>
/// </summary>
public const string EnvelopeId = "envelope_id";
/// <summary>
/// The unique identifier (UUID) of the envelope.
/// Claim name: <c>envelope_uuid</c>
/// </summary>
public const string EnvelopeUuid = "envelope_uuid";
/// <summary>
/// The database primary key of the envelope receiver.
/// Claim name: <c>receiver_id</c>
/// </summary>
public const string ReceiverId = "receiver_id";
/// <summary>
/// The cryptographic signature of the envelope receiver.
/// Together with <see cref="EnvelopeUuid"/>, it forms the unique envelope key via <c>ToEnvelopeKey()</c>.
/// Claim name: <c>receiver_sig</c>
/// </summary>
public const string ReceiverSignature = "receiver_sig";
}
}

View File

@@ -0,0 +1,180 @@
using DigitalData.Auth.Claims;
namespace DigitalData.Auth.Tests.Claims;
[TestFixture]
public class CookieNamesTests
{
private const string DefaultCookieName = "AuthToken";
private const string Key = "NTE3YmI5YzUtNjA4Mi00ZTYxLWFhYTUtOTg0NjM4";
[Test]
public void GetEnvelopeReceiverCookieName_ShouldReturnCorrectFormat()
{
// Arrange
var expected = $"{DefaultCookieName}SignFLOWReceiver.{Key}";
// Act
var result = CookieNames.GetEnvelopeReceiverCookieName(DefaultCookieName, Key);
// Assert
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void GetEnvelopeReceiverCookieName_WithKeyOnly_ShouldUseDefaultCookieName()
{
// Arrange
var expected = $"{DefaultCookieName}SignFLOWReceiver.{Key}";
// Act
var result = CookieNames.GetEnvelopeReceiverCookieName(Key);
// Assert
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void GetEnvelopeKeyOrDefault_ShouldReturnKey_WhenCookieNameIsValid()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(DefaultCookieName, Key);
// Act
var result = CookieNames.GetEnvelopeKeyOrDefault(cookieName, DefaultCookieName);
// Assert
Assert.That(result, Is.EqualTo(Key));
}
[Test]
public void GetEnvelopeKeyOrDefault_WithDefaultParam_ShouldReturnKey_WhenCookieUsesDefaultBase()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(Key); // uses "AuthToken" base
// Act
var result = CookieNames.GetEnvelopeKeyOrDefault(cookieName); // defaultCookieName = "AuthToken"
// Assert
Assert.That(result, Is.EqualTo(Key));
}
[Test]
public void GetEnvelopeKeyOrDefault_ShouldReturnNull_WhenCookieNameHasWrongBase()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName("OtherCookie", Key);
// Act
var result = CookieNames.GetEnvelopeKeyOrDefault(cookieName, DefaultCookieName);
// Assert
Assert.That(result, Is.Null);
}
[Test]
public void GetEnvelopeKeyOrDefault_ShouldReturnNull_WhenCookieNameIsUnrelated()
{
// Act
var result = CookieNames.GetEnvelopeKeyOrDefault("SomeOtherCookie", DefaultCookieName);
// Assert
Assert.That(result, Is.Null);
}
[Test]
public void TryGetEnvelopeKey_WithDefaultCookieName_ShouldReturnTrueAndKey_WhenCookieNameIsValid()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(Key); // uses "AuthToken" base
// Act
var success = CookieNames.TryGetEnvelopeKey(cookieName, out var key);
// Assert
Assert.Multiple(() =>
{
Assert.That(success, Is.True);
Assert.That(key, Is.EqualTo(Key));
});
}
[Test]
public void TryGetEnvelopeKey_WithDefaultCookieName_ShouldReturnFalse_WhenCookieNameHasWrongBase()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName("OtherCookie", Key);
// Act
var success = CookieNames.TryGetEnvelopeKey(cookieName, out var key);
// Assert
Assert.Multiple(() =>
{
Assert.That(success, Is.False);
Assert.That(key, Is.Null);
});
}
[Test]
public void TryGetEnvelopeKey_ShouldReturnTrueAndKey_WhenCookieNameIsValid()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(DefaultCookieName, Key);
// Act
var success = CookieNames.TryGetEnvelopeKey(cookieName, DefaultCookieName, out var key);
// Assert
Assert.Multiple(() =>
{
Assert.That(success, Is.True);
Assert.That(key, Is.EqualTo(Key));
});
}
[Test]
public void TryGetEnvelopeKey_ShouldReturnFalse_WhenCookieNameHasWrongBase()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName("OtherCookie", Key);
// Act
var success = CookieNames.TryGetEnvelopeKey(cookieName, DefaultCookieName, out var key);
// Assert
Assert.Multiple(() =>
{
Assert.That(success, Is.False);
Assert.That(key, Is.Null);
});
}
[Test]
public void TryGetEnvelopeKey_ShouldReturnFalse_WhenCookieNameIsUnrelated()
{
// Act
var success = CookieNames.TryGetEnvelopeKey("SomeOtherCookie", DefaultCookieName, out var key);
// Assert
Assert.Multiple(() =>
{
Assert.That(success, Is.False);
Assert.That(key, Is.Null);
});
}
[Test]
public void GetAndGetOrDefault_ShouldBeInverse_RoundTrip()
{
// Arrange
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(DefaultCookieName, Key);
// Act
var extractedKey = CookieNames.GetEnvelopeKeyOrDefault(cookieName, DefaultCookieName);
// Assert
Assert.That(extractedKey, Is.EqualTo(Key));
}
}

View File

@@ -16,6 +16,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" /> <PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="DigitalData.Core.Security" Version="1.2.3" /> <PackageReference Include="DigitalData.Core.Security" Version="1.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" /> <PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" /> <PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
@@ -23,6 +25,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DigitalData.Auth.Claims\DigitalData.Auth.Claims.csproj" />
<ProjectReference Include="..\DigitalData.Auth.Client\DigitalData.Auth.Client.csproj" /> <ProjectReference Include="..\DigitalData.Auth.Client\DigitalData.Auth.Client.csproj" />
<ProjectReference Include="..\src\DigitalData.Auth.API\DigitalData.Auth.API.csproj" /> <ProjectReference Include="..\src\DigitalData.Auth.API\DigitalData.Auth.API.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Auth.Tests", "D
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Auth.Abstractions", "DigitalData.Auth.Abstractions\DigitalData.Auth.Abstractions.csproj", "{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Auth.Abstractions", "DigitalData.Auth.Abstractions\DigitalData.Auth.Abstractions.csproj", "{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Auth.Claims", "DigitalData.Auth.Claims\DigitalData.Auth.Claims.csproj", "{5AF91476-1897-46D2-B9E0-323EB9D39B15}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -23,18 +25,22 @@ Global
{1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Debug|Any CPU.Build.0 = Debug|Any CPU {1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Release|Any CPU.ActiveCfg = Release|Any CPU {1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Release|Any CPU.Build.0 = Release|Any CPU {1AF05BC2-6F15-420A-85F6-E6F8740CD557}.Release|Any CPU.Build.0 = Release|Any CPU
{521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Debug|Any CPU.ActiveCfg = Release|Any CPU {521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Debug|Any CPU.Build.0 = Release|Any CPU {521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Release|Any CPU.Build.0 = Release|Any CPU {521A2BC0-AEA8-4500-AAA9-1951556EDF9F}.Release|Any CPU.Build.0 = Release|Any CPU
{AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Release|Any CPU.Build.0 = Release|Any CPU {AF517FD9-3EBE-4452-AAEC-DFF17CC270E3}.Release|Any CPU.Build.0 = Release|Any CPU
{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Debug|Any CPU.ActiveCfg = Release|Any CPU {09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Debug|Any CPU.Build.0 = Release|Any CPU {09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Release|Any CPU.ActiveCfg = Release|Any CPU {09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Release|Any CPU.Build.0 = Release|Any CPU {09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C}.Release|Any CPU.Build.0 = Release|Any CPU
{5AF91476-1897-46D2-B9E0-323EB9D39B15}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{5AF91476-1897-46D2-B9E0-323EB9D39B15}.Debug|Any CPU.Build.0 = Release|Any CPU
{5AF91476-1897-46D2-B9E0-323EB9D39B15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AF91476-1897-46D2-B9E0-323EB9D39B15}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -44,6 +50,7 @@ Global
{521A2BC0-AEA8-4500-AAA9-1951556EDF9F} = {C0123B52-5168-4C87-98A0-11A220EC392F} {521A2BC0-AEA8-4500-AAA9-1951556EDF9F} = {C0123B52-5168-4C87-98A0-11A220EC392F}
{AF517FD9-3EBE-4452-AAEC-DFF17CC270E3} = {C0123B52-5168-4C87-98A0-11A220EC392F} {AF517FD9-3EBE-4452-AAEC-DFF17CC270E3} = {C0123B52-5168-4C87-98A0-11A220EC392F}
{09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C} = {C0123B52-5168-4C87-98A0-11A220EC392F} {09FF9BF0-25BB-4EB2-B1B2-6D2873B9538C} = {C0123B52-5168-4C87-98A0-11A220EC392F}
{5AF91476-1897-46D2-B9E0-323EB9D39B15} = {C0123B52-5168-4C87-98A0-11A220EC392F}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D163037-043C-41AE-AB94-C7314F2C38DA} SolutionGuid = {4D163037-043C-41AE-AB94-C7314F2C38DA}

View File

@@ -1,16 +1,27 @@
using DigitalData.Auth.API.Config; using DigitalData.Auth.API.Config;
using Microsoft.AspNetCore.Authorization; using DigitalData.Auth.API.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Auth.API.Models; using DigitalData.Auth.API.Models;
using DigitalData.Auth.API.Services.Contracts; using DigitalData.Auth.API.Services.Contracts;
using DigitalData.Auth.API.Entities; using DigitalData.Auth.Claims;
using DigitalData.Core.DTO; using DigitalData.Core.Abstraction.Application;
using DigitalData.Core.Abstractions.Security.Services; using DigitalData.Core.Abstraction.Application.DTO;
using DigitalData.Core.Abstractions.Security.Extensions; using DigitalData.Core.Abstractions.Security.Extensions;
using DigitalData.Core.Abstractions.Security.Services;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Receivers.Queries;
using EnvelopeGenerator.Domain.Entities;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.Options;
using System.Net;
using System.Text;
namespace DigitalData.Auth.API.Controllers namespace DigitalData.Auth.API.Controllers
{ {
@@ -18,6 +29,8 @@ namespace DigitalData.Auth.API.Controllers
[ApiController] [ApiController]
public class AuthController : ControllerBase public class AuthController : ControllerBase
{ {
private readonly IJwtSignatureHandler<EnvelopeReceiverSecretDto> _erSignatureHandler;
private readonly IJwtSignatureHandler<UserReadDto> _userSignatureHandler; private readonly IJwtSignatureHandler<UserReadDto> _userSignatureHandler;
private readonly IJwtSignatureHandler<Consumer> _consumerSignatureHandler; private readonly IJwtSignatureHandler<Consumer> _consumerSignatureHandler;
@@ -36,7 +49,9 @@ namespace DigitalData.Auth.API.Controllers
private readonly IOptionsMonitor<BackdoorParams> _backdoorMonitor; private readonly IOptionsMonitor<BackdoorParams> _backdoorMonitor;
public AuthController(IJwtSignatureHandler<UserReadDto> userSignatureHandler, IOptions<AuthApiParams> cookieParamsOptions, IAsymmetricKeyPool keyPool, ILogger<AuthController> logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler<Consumer> apiSignatureHandler, IOptionsMonitor<BackdoorParams> backdoorMonitor) private readonly IMediator _mediator;
public AuthController(IJwtSignatureHandler<UserReadDto> userSignatureHandler, IOptions<AuthApiParams> cookieParamsOptions, IAsymmetricKeyPool keyPool, ILogger<AuthController> logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler<Consumer> apiSignatureHandler, IOptionsMonitor<BackdoorParams> backdoorMonitor, IMediator mediator, IJwtSignatureHandler<EnvelopeReceiverSecretDto> erSignatureHandler)
{ {
_apiParams = cookieParamsOptions.Value; _apiParams = cookieParamsOptions.Value;
_userSignatureHandler = userSignatureHandler; _userSignatureHandler = userSignatureHandler;
@@ -47,6 +62,8 @@ namespace DigitalData.Auth.API.Controllers
_consumerService = consumerService; _consumerService = consumerService;
_consumerSignatureHandler = apiSignatureHandler; _consumerSignatureHandler = apiSignatureHandler;
_backdoorMonitor = backdoorMonitor; _backdoorMonitor = backdoorMonitor;
_mediator = mediator;
_erSignatureHandler = erSignatureHandler;
} }
private async Task<IActionResult> CreateTokenAsync(UserLogin login, string consumerName, bool cookie = true) private async Task<IActionResult> CreateTokenAsync(UserLogin login, string consumerName, bool cookie = true)
@@ -210,5 +227,43 @@ namespace DigitalData.Auth.API.Controllers
[HttpGet("check")] [HttpGet("check")]
[Authorize] [Authorize]
public IActionResult Check() => Ok(); public IActionResult Check() => Ok();
[HttpPost("envelope-receiver/{key}")]
public async Task<IActionResult> CreateTokenForEnvelopeReceiver([FromRoute]string key, [FromForm] ReceiverLogin receiverLogin, [FromQuery] bool cookie = true, CancellationToken cancel = default)
{
//find the consumer
var consumer = await _consumerService.ReadByNameAsync("sign-flow");
if (consumer is null)
return Unauthorized();
if (!_keyPool.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor))
return StatusCode(StatusCodes.Status500InternalServerError);
// find receiver
var er = await _mediator.Send(new ReadEnvelopeReceiverSecretQuery()
{
Key = key
}, cancel);
if (er is null)
return NotFound();
// check acccess code
if (er.AccessCode != receiverLogin.AccessCode)
return Unauthorized();
// create token
var token = _erSignatureHandler.WriteToken(er, descriptor);
//set cookie
if (cookie)
{
var cookieOptions = consumer.CookieOptions ?? _apiParams.DefaultCookieOptions;
Response.Cookies.Append(CookieNames.GetEnvelopeReceiverCookieName(_apiParams.DefaultCookieName, key), token, cookieOptions.Create(lifetime: descriptor.Lifetime));
return Ok();
}
else
return Ok(token);
}
} }
} }

View File

@@ -1,29 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net8.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Version>1.2.0</Version> <Version>1.4.1</Version>
<AssemblyVersion>1.2.0</AssemblyVersion> <AssemblyVersion>1.4.1</AssemblyVersion>
<FileVersion>1.2.0</FileVersion> <FileVersion>1.4.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="3.4.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="4.3.0" />
<PackageReference Include="DigitalData.Core.Abstractions.Security" Version="1.0.0" /> <PackageReference Include="DigitalData.Core.Abstractions.Security" Version="1.0.0" />
<PackageReference Include="DigitalData.Core.Application" Version="3.2.0" /> <PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
<PackageReference Include="DigitalData.Core.Security" Version="1.2.2" /> <PackageReference Include="DigitalData.Core.Security" Version="1.2.2" />
<PackageReference Include="EnvelopeGenerator" Version="1.2.0.3" />
<PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.1" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.1" />
<PackageReference Include="NLog" Version="5.4.0" /> <PackageReference Include="NLog" Version="5.4.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.4.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.4.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
<PackageReference Include="UserManager.Application" Version="3.1.2" />
<PackageReference Include="UserManager.Domain" Version="3.0.1" />
<PackageReference Include="UserManager.Infrastructure" Version="3.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
@@ -31,11 +32,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.17" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\DigitalData.Auth.Abstractions\DigitalData.Auth.Abstractions.csproj" /> <ProjectReference Include="..\..\DigitalData.Auth.Abstractions\DigitalData.Auth.Abstractions.csproj" />
<ProjectReference Include="..\..\DigitalData.Auth.Claims\DigitalData.Auth.Claims.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,99 @@
using DigitalData.Core.Exceptions;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace DigitalData.Auth.API.Middleware;
/// <summary>
/// Middleware for handling exceptions globally in the application.
/// Captures exceptions thrown during the request pipeline execution,
/// logs them, and returns an appropriate HTTP response with a JSON error details.
/// </summary>
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
/// </summary>
/// <param name="next">The next middleware in the request pipeline.</param>
/// <param name="logger">The logger instance for logging exceptions.</param>
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
/// <summary>
/// Invokes the middleware to handle the HTTP request.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context); // Continue down the pipeline
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, _logger);
}
}
/// <summary>
/// Handles exceptions by logging them and writing an appropriate JSON response.
/// </summary>
/// <param name="context">The HTTP context of the current request.</param>
/// <param name="exception">The exception that occurred.</param>
/// <param name="logger">The logger instance for logging the exception.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
private static async Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
context.Response.ContentType = "application/json";
ValidationProblemDetails? details = null;
switch (exception)
{
case BadRequestException badRequestEx:
if (badRequestEx.InnerException is not null)
{
logger.LogError(
badRequestEx.InnerException,
"BadRequestException inner exception captured.");
}
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
details = new()
{
Title = "Bad Procedure",
Detail = badRequestEx.Message
};
break;
case NotFoundException notFoundEx:
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
details = new()
{
Title = "Not Found",
Detail = notFoundEx.Message
};
break;
default:
logger.LogError(exception, "Unhandled exception occurred.");
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
details = new()
{
Title = "Internal Server Error",
Detail = "An unexpected error occurred. Please try again later."
};
break;
}
if (details is not null)
await context.Response.WriteAsJsonAsync(details);
}
}

View File

@@ -0,0 +1,4 @@
namespace DigitalData.Auth.API.Models
{
public record ReceiverLogin(string AccessCode);
}

View File

@@ -1,6 +1,7 @@
using DigitalData.Auth.API.Config; using DigitalData.Auth.API.Config;
using DigitalData.Auth.API.Entities; using DigitalData.Auth.API.Entities;
using DigitalData.Auth.API.Hubs; using DigitalData.Auth.API.Hubs;
using DigitalData.Auth.API.Middleware;
using DigitalData.Auth.API.Services; using DigitalData.Auth.API.Services;
using DigitalData.Core.Abstractions.Security.Extensions; using DigitalData.Core.Abstractions.Security.Extensions;
using DigitalData.Core.Abstractions.Security.Services; using DigitalData.Core.Abstractions.Security.Services;
@@ -8,13 +9,20 @@ using DigitalData.Core.Application;
using DigitalData.Core.Security.Extensions; using DigitalData.Core.Security.Extensions;
using DigitalData.UserManager.Application; using DigitalData.UserManager.Application;
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.DependencyInjection;
using DigitalData.Auth.Claims;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.DependencyInjection;
using EnvelopeGenerator.Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using NLog; using NLog;
using NLog.Web; using NLog.Web;
using System.Security.Claims;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized."); logger.Info("Logging initialized.");
@@ -23,11 +31,13 @@ try
{ {
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
builder.Host.UseNLog();
builder.Configuration.AddJsonFile("consumer-repository.json", true, true); if (!builder.Environment.IsDevelopment())
{
builder.Logging.ClearProviders();
builder.Host.UseNLog();
}
builder.Configuration.AddJsonFile("consumer-repository.json", true, true); builder.Configuration.AddJsonFile("consumer-repository.json", true, true);
@@ -43,25 +53,54 @@ try
builder.Services.AddAuthService(config); builder.Services.AddAuthService(config);
builder.Services.AddRSAPool(config.GetSection("CryptParams")); builder.Services.AddRSAPool(config.GetSection("CryptParams"));
builder.Services.AddJwtSignatureHandler<Consumer>(api => new Dictionary<string, object> builder.Services.AddJwtSignatureHandler<Consumer>(api => new Dictionary<string, object>
{ {
{ JwtRegisteredClaimNames.Sub, api.Id }, { JwtRegisteredClaimNames.Sub, api.Id },
{ JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() } { JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
}); });
builder.Services.AddJwtSignatureHandler<UserReadDto>(user => new Dictionary<string, object>
{ var commonUserRoles = config.GetSection("CommonUserRoles").Get<string[]>()?.Where(r => !string.IsNullOrWhiteSpace(r)).ToArray() ?? Array.Empty<string>();
builder.Services.AddJwtSignatureHandler<UserReadDto>(user =>
{
var claims = new Dictionary<string, object>
{
{ JwtRegisteredClaimNames.Sub, user.Id }, { JwtRegisteredClaimNames.Sub, user.Id },
{ JwtRegisteredClaimNames.UniqueName, user.Username }, { JwtRegisteredClaimNames.UniqueName, user.Username },
{ JwtRegisteredClaimNames.Email, user.Email ?? string.Empty }, { JwtRegisteredClaimNames.Email, user.Email ?? string.Empty },
{ JwtRegisteredClaimNames.GivenName, user.Prename ?? string.Empty }, { JwtRegisteredClaimNames.GivenName, user.Prename ?? string.Empty },
{ JwtRegisteredClaimNames.FamilyName, user.Name ?? string.Empty }, { JwtRegisteredClaimNames.FamilyName, user.Name ?? string.Empty },
{ JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() } { JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
}); };
if (commonUserRoles.Length > 0)
claims.Add(ClaimTypes.Role, commonUserRoles);
return claims;
});
builder.Services.AddJwtSignatureHandler<EnvelopeReceiverSecretDto>(er =>
{
var claims = new Dictionary<string, object>
{
{ JwtRegisteredClaimNames.Sub, (er.Envelope!.Uuid, er.Receiver!.Signature).ToEnvelopeKey() },
{ EnvelopeClaimNames.EnvelopeId, er.Envelope.Id },
{ EnvelopeClaimNames.EnvelopeUuid, er.Envelope.Uuid },
{ EnvelopeClaimNames.ReceiverId, er.Receiver.Id },
{ EnvelopeClaimNames.ReceiverSignature, er.Receiver.Signature },
{ JwtRegisteredClaimNames.Email, er.Receiver.EmailAddress ?? string.Empty },
{ JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ ClaimTypes.Role, "receiver" }
};
return claims;
});
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions")); builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
builder.Services.AddSignalR(); builder.Services.AddSignalR();
var cnn_str = builder.Configuration.GetConnectionString("Default") ?? throw new InvalidOperationException("Default connection string is not found."); var cnn_str = builder.Configuration.GetConnectionString("Default") ?? throw new InvalidOperationException("Default connection string is not found.");
builder.Services.AddUserManager(cnn_str); builder.Services.AddLocalization();
builder.Services.AddControllers(); builder.Services.AddControllers();
@@ -133,8 +172,34 @@ try
}; };
}); });
// Add envelope generator services
builder.Services.AddEnvelopeGenerator(config,
infrastructureOptions: opt =>
{
opt.AddDbTriggerParams(config);
opt.AddDbContext((provider, options) =>
{
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
options.UseSqlServer(cnn_str)
.LogTo(log => logger.LogInformation("{log}", log), Microsoft.Extensions.Logging.LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
},
options: opt =>
{
opt.SqlCacheOptions = new()
{
ConnectionString = cnn_str,
SchemaName = "dbo",
TableName = "TBDD_CACHE"
};
});
var app = builder.Build(); var app = builder.Build();
app.UseMiddleware<ExceptionHandlingMiddleware>();
issuerSigningKeyInitiator = new Lazy<SecurityKey>(() => issuerSigningKeyInitiator = new Lazy<SecurityKey>(() =>
{ {
var factory = app.Services.GetRequiredService<IAsymmetricKeyPool>(); var factory = app.Services.GetRequiredService<IAsymmetricKeyPool>();

View File

@@ -14,7 +14,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "http://localhost:5075", "applicationUrl": "http://localhost:9090",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
@@ -24,7 +24,7 @@
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7192;http://localhost:5075", "applicationUrl": "https://localhost:9090;http://localhost:5075",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }

View File

@@ -15,6 +15,12 @@
"Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works" "Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works"
}, },
"Issuer": "auth.digitaldata.works", "Issuer": "auth.digitaldata.works",
"CommonUserRoles": [
"Admin",
"UserManager",
"ContentManager",
"Signatory"
],
"LocalConsumer": { "LocalConsumer": {
"Id": -1, "Id": -1,
"Name": "auth-flow", "Name": "auth-flow",
@@ -55,7 +61,7 @@
{ {
"Id": "f3c0881b-c349-442a-ac24-d02da0798abd", "Id": "f3c0881b-c349-442a-ac24-d02da0798abd",
"Issuer": "auth.digitaldata.works", "Issuer": "auth.digitaldata.works",
"Audience": "sign-flow-gen.digitaldata.works", "Audience": "sign-flow.digitaldata.works",
"IsEncrypted": true, "IsEncrypted": true,
"Lifetime": "12:00:00" "Lifetime": "12:00:00"
} }
@@ -111,5 +117,13 @@
"writeTo": "criticalLogs" "writeTo": "criticalLogs"
} }
] ]
},
"DbTriggerParams": {
"Envelope": [ "TBSIG_ENVELOPE_AFT_INS" ],
"History": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
"EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
"EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
"Receiver": [],
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
} }
} }

View File

@@ -14,8 +14,8 @@
}, },
{ {
"Id": 2, "Id": 2,
"Name": "sign-flow-gen", "Name": "sign-flow",
"Audience": "sign-flow-gen.digitaldata.works", "Audience": "sign-flow.digitaldata.works",
"Password": "Gpm63fny0W63Klc2eWC" "Password": "Gpm63fny0W63Klc2eWC"
} }
] ]