Egov.Fod.BackComponents
10.0.6
Prefix Reserved
dotnet add package Egov.Fod.BackComponents --version 10.0.6
NuGet\Install-Package Egov.Fod.BackComponents -Version 10.0.6
<PackageReference Include="Egov.Fod.BackComponents" Version="10.0.6" />
<PackageVersion Include="Egov.Fod.BackComponents" Version="10.0.6" />
<PackageReference Include="Egov.Fod.BackComponents" />
paket add Egov.Fod.BackComponents --version 10.0.6
#r "nuget: Egov.Fod.BackComponents, 10.0.6"
#:package Egov.Fod.BackComponents@10.0.6
#addin nuget:?package=Egov.Fod.BackComponents&version=10.0.6
#tool nuget:?package=Egov.Fod.BackComponents&version=10.0.6
Fod.BackComponents
Standard backend components and integrations for E-Government (FOD) services in .NET. This library provides a unified interface for MDocs, MNotify, MSign, MPower, and MConnect services.
Features
- MDocs Integration — Document generation (PDF) from templates, QR code injection, upload to MDocs, and S3-compatible object storage (Minio, RustFS) for files.
- MNotify Integration — Sending notifications, error alerts, and confirmation messages via MNotify.
- MSign Integration — Digital signature of PDF documents and arbitrary data hashes via MSign SOAP service.
- MPower Integration — Authorization lookup, verification, and power-of-attorney checks.
- MConnect Integration — Inter-governmental data exchange: person verification, organization lookup, and entity-relationship checks via SOAP ambassador.
Installation
Install via NuGet:
dotnet add package Egov.Fod.BackComponents
Setup and Registration
Register the services in your Program.cs (or Startup.cs) using the provided Dependency Injection extensions from Fod.BackComponents.DependencyInjection.
1. MDocs / File Storage
Registers IFileStorage with an S3-compatible object storage (Minio, RustFS, ...) and the MDocs API clients.
builder.Services.AddFileStorage(builder.Configuration);
Configuration (appsettings.json):
{
"MDocs": {
"BaseAddress": "https://mdocs.api.example.com",
"FrontendAddress": "https://mdocs.example.com"
},
"ObjectStorage": {
"Endpoint": "storage.example.com:9000",
"AccessKey": "YOUR_ACCESS_KEY",
"SecretKey": "YOUR_SECRET_KEY",
"BucketName": "your-bucket",
"WithSsl": "true"
}
}
All keys above are required; the registration will throw ArgumentException if any is missing. Endpoint is
host[:port] of the storage's S3 API, without a scheme (WithSsl decides it).
The section was called Minio before, and that name still works. ObjectStorage is read first; only when it has
nothing in it, Minio is read, so a host that has not moved keeps its configuration untouched. The choice is of the
whole section, never key by key: if ObjectStorage has any value, Minio is ignored, and a key missing from
ObjectStorage is an error that names ObjectStorage (it is not taken from Minio). The same goes for environment
variables: move them together (ObjectStorage__AccessKey, ObjectStorage__SecretKey, ... instead of
Minio__AccessKey, ...). A host that needs to know the section in use (to decide whether to register the storage) calls
ObjectStorageConfiguration.GetSection(configuration); its Key says which one it is.
The object methods of IFileStorage (UploadObjectAsync, DownloadObjectAsync, DeleteObjectAsync, MoveObjectAsync) work the same on any S3-compatible
storage; UploadMinioFileAsync, the previous name of the upload, still works as an [Obsolete] forward. They were checked against RustFS (upload, multipart upload, download, move, delete, a missing object, a
lifecycle rule); whether it actually carries out the expiry was not.
Document library (the signed-in user's MDocs documents)
For the "search in library" tab of the attachments step, register the library on its own instead of AddFileStorage: it needs no object storage and no MDocs:FrontendAddress.
builder.Services.AddSystemCertificate(builder.Configuration.GetSection("Certificate")); // the MDocs client throws without it
builder.Services.AddMDocsDocumentLibrary(builder.Configuration);
// ...
app.UseAuthentication();
app.UseAuthorization();
app.MapDocumentLibraryEndpoints();
{ "MDocs": { "BaseAddress": "https://mdocs.api.example.com" } }
MapDocumentLibraryEndpoints maps GET api/fod/documents/library?search=&page=&pageSize=. It requires an authenticated user and always lists that user's documents: the IDNP is read from the session (the Username claim MPass sets, or the name identifier), never from the request. pageSize is capped at 50. It returns UserDocumentsPage (from Fod.ServiceModels.Attachments). IMDocsDocumentLibrary.GetUserDocumentsAsync is the service behind it; only an IDNP (starts with 2) or an IDNO (starts with 1) is accepted, never the public principal. A host that also uses AddFileStorage can call both: the MDocs client is registered once.
In the WASM client add AddDocumentLibraryClientHttp(baseAddress) (Fod.ServiceComponents) so FodAttachments fills the tab from that endpoint.
Attachment uploads (files picked on the user's computer)
AddFileStorage also registers the store for the files a user picks on their computer in the attachments step. They go to a temporary folder of the object storage bucket and the wizard carries only a reference (AttachmentFileInfo.ServerId), never the file. Documents from the MDocs library need none of this: their reference is the MDocs document id.
builder.Services.AddFileStorage(builder.Configuration); // brings the object storage, so the upload store too
// ...
app.UseAuthentication();
app.UseAuthorization();
app.MapAttachmentUploadEndpoints();
MapAttachmentUploadEndpoints maps POST api/fod/documents/uploads (multipart field file, answers AttachmentUploadResult with the serverId) and DELETE api/fod/documents/uploads?id=<serverId>. Both work signed in or not. A signed-in user owns their uploads by the IDNP from the session; anyone else (for example the first step of a wizard, before the requestor is known) by a random id (anon- + 32 hex digits) the server hands out in an HttpOnly, SameSite=Lax cookie (fod-upload-owner, one day). The owner is never taken from the request body or from what the user types. The reference is the object path temp/attachments/<idnp or anonymous id>/<upload id>/<clean file name>: a reference works only for the owner whose folder it is in, and DELETE of anything else does nothing. Because the endpoint is open to anonymous callers, rate-limit it in the host and keep the lifecycle rule below: the limits of one upload do not bound how many. Someone who signs in after uploading is a different owner (IDNP) than before (cookie). In a server-rendered circuit (no HttpContext once connected) an anonymous owner lives as long as the circuit unless the cookie was already set by an earlier request. The server refuses empty files, files over the limit and blocked extensions whatever the browser says.
Limits, all optional (AttachmentUploads section):
{
"AttachmentUploads": {
"MaxFileSize": 20971520,
"AllowedExtensions": [".pdf", ".jpg", ".png"],
"BlockedExtensions": [".exe", ".dll", ".bat", ".cmd", ".com", ".msi", ".scr", ".ps1", ".sh", ".vbs", ".js", ".jar", ".apk"],
"TempPrefix": "temp/attachments"
}
}
Abandoned uploads stay in the temporary folder: add a lifecycle (expiry) rule for the TempPrefix folder in the bucket of the object storage (for example expire after a day). IAttachmentUploadStore.ReadAsync gives the step that submits the request the file back (only to its owner); IFileStorage.MoveObjectAsync moves it out of the temporary folder.
In the WASM client add AddAttachmentUploadClientHttp(baseAddress) (Fod.ServiceComponents): FodAttachments then uploads each picked file itself, shows the progress and deletes the stored copy when the user removes the file.
2. MNotify
Registers INotificationService.
builder.Services.AddMNotify(builder.Configuration);
Configuration:
{
"MNotify": {
"BaseAddress": "https://mnotify.api.example.com"
}
}
3. MSign
Registers ISignatureService. You must supply your own implementation (typically inheriting SignatureServiceBase) to handle the persistence hooks.
builder.Services.AddMSign<MySignatureService>(builder.Configuration);
Configuration:
{
"MSign": {
"ApiAddress": "https://msign.api.example.com",
"FrontendAddress": "https://msign.example.com",
"ServiceRootUrl": "https://your-app.example.com"
}
}
All three keys are required.
4. MPower
Registers IMPowerService. Uses an HttpClient with optional client-certificate authentication via SystemCertificateOptions.
builder.Services.AddMPower(builder.Configuration);
Configuration:
{
"MPower": {
"BaseAddress": "https://mpower.api.example.com"
}
}
5. MConnect
Registers IFodMConnectService with the FOD SOAP ambassador.
builder.Services.AddFodGenericMConnect(builder.Configuration);
Configuration:
{
"MConnect": {
"FodAmbassadorAddress": "https://mconnect.example.com",
"Options": {
"CallingEntity": "YOUR_IDNO",
"CallBasis": "Law/Regulation reference",
"CallReason": "Purpose of the call"
}
}
}
FodAmbassadorAddress and all three Options fields (CallingEntity, CallBasis, CallReason) are required.
Library Usage
MDocs: Document Generation & Storage (IFileStorage)
IFileStorage provides eight methods:
| Method | Description |
|---|---|
GenerateDocumentAsync<T>(model, documentTypeCode, fileName, language?, ct) |
Generates a PDF from a template and returns a FileStreamResult. |
GenerateDocumentWithQrAsync<T>(model, templateName, fileName, language?, ct) |
Generates a PDF with an auto-injected QR code and returns (FileStreamResult, Guid). |
UploadFileToMDocsAsync(model, ct) |
Uploads an existing file stream to MDocs and returns the reservation Guid. |
GenerateAndUploadToMDocsAsync<T>(model, ct) |
Generates a PDF and uploads it to MDocs in one step. |
UploadObjectAsync(storePath, stream, ct) |
Uploads a file directly to the object storage. |
DownloadObjectAsync(storePath, destination, ct) |
Streams an object into a stream; a failure (not an exception) when it does not exist. |
DeleteObjectAsync(storePath, ct) |
Deletes an object; an object that is already gone is not an error. |
MoveObjectAsync(sourcePath, destinationPath, ct) |
Moves an object inside the bucket (copy, then delete the source); a failure when the source does not exist. |
Example — Generate a PDF:
public class MyDocumentService(IFileStorage fileStorage)
{
public async Task<FileStreamResult> GeneratePdf(MyData data, CancellationToken ct)
{
var result = await fileStorage.GenerateDocumentAsync(
data, "TEMPLATE_CODE", "Document.pdf", "ro", ct);
return result.Value;
}
}
Example — Generate with QR code and upload:
To embed a QR code, implement IHasQrCode on your model:
public class MyDocModel : IHasQrCode
{
public string Name { get; set; } = string.Empty;
public string? QrCode { get; set; } // populated automatically
}
Then call:
var (fileResult, reservationId) = (await fileStorage.GenerateDocumentWithQrAsync(
myDocModel, "TEMPLATE_CODE", "Doc.pdf", cancellationToken: ct)).Value;
Example — Generate and upload in one step:
var model = new GenerateAndUploadToMDocsModel<MyData>
{
Model = myData,
Language = "ro",
DocumentTypeCode = "TEMPLATE_CODE",
FileName = "Contract.pdf",
Principal = "your-principal",
CreatedBy = "your-system",
Recipients = [ new DocumentRecipient { For = "recipientId", Permission = Permission.View } ]
};
var docId = await fileStorage.GenerateAndUploadToMDocsAsync(model, ct);
Example — Upload existing file to MDocs:
var uploadModel = new UploadToMDocsModel
{
Stream = myMemoryStream,
DocumentTypeCode = "DOC_TYPE",
FileName = "Report.pdf",
Principal = "your-principal",
CreatedBy = "your-system",
Recipients = [ new DocumentRecipient { For = "recipientId", Permission = Permission.View } ]
};
var reservationId = await fileStorage.UploadFileToMDocsAsync(uploadModel, ct);
MNotify: Sending Notifications (INotificationService)
| Method | Description |
|---|---|
SendNotificationAsync(request, ct) |
Sends a general notification. Returns the notification Guid. |
SendErrorNotificationAsync(errorMessage, ct) |
Sends an error notification. |
SendConfirmationNotificationAsync(ct, message?, templateId?) |
Sends a confirmation notification using an optional template. |
Example:
public class MyNotifyService(INotificationService notificationService)
{
public async Task SendError(string message, CancellationToken ct)
{
await notificationService.SendErrorNotificationAsync(message, ct);
}
public async Task SendConfirmation(Guid templateId, CancellationToken ct)
{
await notificationService.SendConfirmationNotificationAsync(ct, "Your request was approved.", templateId);
}
}
MSign: Digital Signatures (ISignatureService)
ISignatureService has two groups of methods:
Service methods (implemented by SignatureServiceBase):
| Method | Description |
|---|---|
SignDocumentAsync(request, ct) |
Submits a PDF to MSign for signing; returns a redirect URL. |
SignHashAsync<T>(model, ct) |
Serialises an object, computes SHA-256, submits for hash-signing; returns a redirect URL. |
GetSignedObjectAsync(mSignId, ct) |
Retrieves the signed content (PDF or signature bytes) from MSign. |
GenerateHash<T>(obj, ct) |
Computes the SHA-256 hash of the JSON-serialised object. |
Abstract hooks (you must implement these in your subclass):
| Method | Description |
|---|---|
SaveSignedDocument(model, ct) |
Persist the signed PDF bytes after a successful callback. |
SaveSignableObject(model, ct) |
Persist the JSON/XML snapshot and hash when a sign request is created. |
SaveHashSignature(model, ct) |
Persist the signature bytes after a successful hash callback. |
Example — Custom implementation:
public class MySignatureService : SignatureServiceBase
{
public MySignatureService(ILogger<MySignatureService> logger) : base(logger) { }
public override Task<Result<bool>> SaveSignedDocument(SaveSignedDocumentModel model, CancellationToken ct)
{
// Persist model.Content (signed PDF bytes) to your database/storage
return Task.FromResult(Result<bool>.Success(true));
}
public override Task<Result<bool>> SaveSignableObject(SignableObjectModel model, CancellationToken ct)
{
// Persist the serialised object snapshot and hash
return Task.FromResult(Result<bool>.Success(true));
}
public override Task<Result<bool>> SaveHashSignature(SaveSignedHashModel model, CancellationToken ct)
{
// Persist the hash signature bytes
return Task.FromResult(Result<bool>.Success(true));
}
}
Usage:
// Sign a PDF document — redirect the user to the returned URL
var redirectUrl = (await signatureService.SignDocumentAsync(request, ct)).Value;
// Sign an object by hash
var redirectUrl = (await signatureService.SignHashAsync(objectSignatureModel, ct)).Value;
MPower: Authorization Checks (IMPowerService)
| Method | Description |
|---|---|
GetAuthorizationByCodeAsync(authorizationCode, ct) |
Gets authorization details by its unique code. |
GetAuthorizationByActorsAsync(typeCode, authorizingIdnx, authorizedIdnx, ct) |
Gets authorization by actor IDNXs. |
GetAuthorizationsAsync(typeCode, authorizingIdnx, authorizedIdnx, ct) |
Gets a list of authorizations for the specified actors. |
VerifyAuthorizationAsync(typeCode, authorizationCode, authorizingIdnx, authorizedIdnx, ct) |
Verifies if a specific authorization is valid. Returns bool. |
Example:
public class MyAuthService(IMPowerService mPowerService)
{
public async Task<bool> CanAct(string typeCode, string code, string principalIdnx, string agentIdnx, CancellationToken ct)
{
var result = await mPowerService.VerifyAuthorizationAsync(typeCode, code, principalIdnx, agentIdnx, ct);
return result.Value;
}
public async Task<AuthorizationModel> GetAuthorization(string code, CancellationToken ct)
{
var result = await mPowerService.GetAuthorizationByCodeAsync(code, ct);
return result.Value;
}
}
MConnect: Data Integration (IFodMConnectService)
| Method | Description |
|---|---|
VerifyPersonAsync(request) |
Verifies person data via MConnect. |
GetOrganizationNameAsync(request) |
Retrieves an organization name. |
VerifyEntitiesRelationshipAsync(request) |
Verifies the relationship between two entities. |
Example:
public class MyMConnectService(IFodMConnectService mConnectService)
{
public async Task<FodVerifyPersonResponse> VerifyPerson(FodVerifyPersonRequest request)
{
var result = await mConnectService.VerifyPersonAsync(request);
return result.Value;
}
public async Task<FodGetOrganizationNameResponse> GetOrgName(FodGetOrganizationNameRequest request)
{
var result = await mConnectService.GetOrganizationNameAsync(request);
return result.Value;
}
}
License
This project is licensed under the MIT License — see the LICENSE file for details.
Changelog
See CHANGELOG.md for what changed in each release.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Egov.Fod.ServiceModels (>= 10.0.4)
- Egov.Integrations.MDocs (>= 10.0.3)
- Egov.Integrations.MNotify (>= 10.0.2)
- Egov.Integrations.MSign.Soap (>= 10.0.2)
- FluentValidation.DependencyInjectionExtensions (>= 12.1.1)
- Minio (>= 7.0.0)
- QRCoder (>= 1.8.0)
- System.ServiceModel.Http (>= 10.0.652802)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.