NcDanilo.UserLibrary 1.2.3

dotnet add package NcDanilo.UserLibrary --version 1.2.3
                    
NuGet\Install-Package NcDanilo.UserLibrary -Version 1.2.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NcDanilo.UserLibrary" Version="1.2.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NcDanilo.UserLibrary" Version="1.2.3" />
                    
Directory.Packages.props
<PackageReference Include="NcDanilo.UserLibrary" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NcDanilo.UserLibrary --version 1.2.3
                    
#r "nuget: NcDanilo.UserLibrary, 1.2.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NcDanilo.UserLibrary@1.2.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NcDanilo.UserLibrary&version=1.2.3
                    
Install as a Cake Addin
#tool nuget:?package=NcDanilo.UserLibrary&version=1.2.3
                    
Install as a Cake Tool

NcDanilo.UserLibrary

A .NET class library that provides ready-to-use services and repositories for user management, role management, email verification, and JWT-based authentication (access token + refresh token) backed by a SQL Server database.

NuGet License .NET


Table of Contents


What's New in 1.2.3

Email Verification

Users must verify their email address after registration before they can log in. A cryptographically-random 6-digit code is generated at registration time and must be submitted to GET /user/verify. Login is blocked with InvalidOperationException until verification is complete.

Admin-aware Registration

UserService now reads AdminEmail from configuration and automatically assigns the admin role when that email registers. All other users receive the user role. The role no longer needs to be passed in the registration request.

One-liner Dependency Injection

A new AddUserLibrary extension method registers all repositories, services, and controllers in a single call. The built-in UserController is also registered automatically via AddApplicationPart.

Automatic Database Initialization

DatabaseInitializer (registered as a singleton) creates the target database if it does not exist, creates all tables, and seeds the admin and user roles. The Users table now includes VerificationCode and IsVerified columns.

New REST Endpoints

PUT /user/status, PUT /user/password, DELETE /user, and GET /user/verify are all now exposed by the built-in UserController.

New Repository Method

GetUserNotVerified returns the list of users who have not yet completed email verification.


Features

  • User management — create, read (by ID or email), update role, update password, soft-delete, list unverified users.
  • Email verification — 6-digit code generated at registration; login blocked until verified.
  • Role management — create, read, update, soft-delete roles; admin/user seeded automatically.
  • JWT access tokens — signed with HMAC-SHA256, expire after 15 minutes, carrying standard claims (id, email, given_name, surname, role).
  • Refresh tokens — persisted to SQL Server, expire after 7 days, support revocation.
  • Password hashing — passwords are securely hashed with BCrypt before storage.
  • Automatic DB setupDatabaseInitializer creates the database and all required tables on startup.
  • Fully async — all I/O operations are async/await with CancellationToken support.
  • Interface-driven — every component is hidden behind an interface, making it easy to mock and test.

Target Frameworks

Framework Version
.NET 10.0

Installation

Install via the .NET CLI:

dotnet add package NcDanilo.UserLibrary

Or search for NcDanilo.UserLibrary in the Visual Studio NuGet Package Manager UI.


Configuration

The library reads its settings from an AppConfiguration object injected via IOptions<AppConfiguration>.

Property Description
ConnectionString SQL Server connection string
SecretKey Secret used to sign JWT tokens (≥ 32 characters recommended)
Issuer JWT iss claim value
Audience JWT aud claim value
AdminEmail The email address that receives the admin role on first registration

appsettings.json example

{
  "AppConfiguration": {
    "ConnectionString": "Server=localhost;Database=MyDb;Trusted_Connection=True;",
    "SecretKey": "your-very-secret-key-at-least-32-chars",
    "Issuer": "https://yourapp.com",
    "Audience": "https://yourapp.com",
    "AdminEmail": "admin@yourapp.com"
  }
}

Database Setup

From v1.2.3, manual table creation is no longer required. Call IDatabaseInitializer.Initialize() at startup and the library will create the database, all tables, and seed the admin / user roles automatically. See Database Initialization.

If you prefer to manage the schema yourself, the expected tables are listed below.

Role

CREATE TABLE [dbo].[Role] (
    [Id]       UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
    [RoleName] NVARCHAR(100)    NOT NULL,
    [Status]   INT              NOT NULL DEFAULT 0  -- 0 = Enabled, 1 = Disabled
);

Users

CREATE TABLE [dbo].[Users] (
    [Id]               UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
    [FirstName]        NVARCHAR(100)    NOT NULL,
    [LastName]         NVARCHAR(100)    NOT NULL,
    [Email]            NVARCHAR(200)    NOT NULL UNIQUE,
    [Password]         NVARCHAR(200)    NOT NULL,  -- BCrypt hash
    [Status]           INT              NOT NULL DEFAULT 0,
    [BirthDate]        DATE             NOT NULL,
    [RoleId]           UNIQUEIDENTIFIER NOT NULL REFERENCES [dbo].[Role]([Id]),
    [VerificationCode] INT              NULL,       -- 6-digit code, NULL after verification
    [IsVerified]       BIT              NOT NULL DEFAULT 0,
    [AccessTokenId]    UNIQUEIDENTIFIER NULL,
    [RefreshTokenId]   UNIQUEIDENTIFIER NULL
);

RefreshToken

CREATE TABLE [dbo].[RefreshToken] (
    [Id]         UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
    [UserId]     UNIQUEIDENTIFIER NOT NULL REFERENCES [dbo].[Users]([Id]),
    [Token]      NVARCHAR(500)    NOT NULL,
    [Expiration] DATETIME2        NOT NULL,
    [IsRevoked]  BIT              NOT NULL DEFAULT 0
);

Note: "Delete" operations on both Users and Roles are implemented as soft deletes — the Status column is set to 1 (Disabled) rather than removing the row.


Getting Started

Registering Services (Dependency Injection)

Use the AddUserLibrary extension method to register everything in one call. It configures AppConfiguration, registers all repositories and services as scoped, registers DatabaseInitializer as a singleton, and auto-discovers the built-in UserController.

// Program.cs
using UserLibrary.Statics;

builder.Services.AddUserLibrary(builder.Configuration);

Make sure app.MapControllers() is called so the built-in endpoints are routed.


Database Initialization

Call Initialize() once at application startup before handling requests:

// Program.cs
using UserLibrary.Interface;

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var dbInit = scope.ServiceProvider.GetRequiredService<IDatabaseInitializer>();
    dbInit.Initialize();
}

app.Run();

Initialize() is idempotent — it uses IF NOT EXISTS guards and is safe to call on every startup.


User Registration

Registration no longer requires a role — it is assigned automatically based on AdminEmail in configuration.

var newUser = new UserDto
{
    FirstName = "John",
    LastName  = "Doe",
    Email     = "john.doe@example.com",
    Password  = "P@ssw0rd!",
    BirthDate = new DateTime(1990, 1, 1)
};

UserDto registered = await userService.RegisterUser(newUser, CancellationToken.None);
// A 6-digit VerificationCode has been generated and stored.
// The code must be sent to the user (e.g. via email) for them to verify.

The returned UserDto contains the VerificationCode so your application can forward it to the user. The Password field is always null in responses after hashing.


Email Verification

After registration the user must verify their email before logging in:

UserDto verified = await userService.VerifyEmail(
    "john.doe@example.com",
    123456,                  // the 6-digit code the user received
    CancellationToken.None);

Console.WriteLine(verified.IsVerified); // true

Throws KeyNotFoundException if the email is not found, ArgumentException if already verified, or InvalidOperationException if the code does not match.


User Login

UserDto loggedIn = await userService.LoginUser(
    "john.doe@example.com",
    "P@ssw0rd!",
    CancellationToken.None);

Console.WriteLine(loggedIn.AccessToken.Token);       // JWT string
Console.WriteLine(loggedIn.AccessToken.Expiration);  // UTC +15 minutes
Console.WriteLine(loggedIn.RefreshToken.Token);      // Refresh token string

Throws:

  • UnauthorizedAccessException — invalid credentials.
  • InvalidOperationException — user not yet verified, or token generation failure.
  • ArgumentException — user account is disabled.

Update User Role

Requires admin JWT. Changes the role assigned to a user.

UserDto updated = await userService.UpdateUserStatus(
    "john.doe@example.com",
    "admin",
    CancellationToken.None);

Update User Password

Requires admin JWT. Hashes and persists a new password.

UserDto updated = await userService.UpdateUserPassword(
    "john.doe@example.com",
    "NewP@ssw0rd!",
    CancellationToken.None);

Delete User

Soft-deletes a user (sets Status = Disabled). Requires admin JWT.

UserDto deleted = await userService.DeleteUser(
    "john.doe@example.com",
    CancellationToken.None);

Console.WriteLine(deleted.Status); // Disabled

Role Management

// Create
var role = new Role { Id = Guid.NewGuid(), RoleName = "moderator", Status = Status.Enabled };
bool created = await roleRepository.CreateRole(role, CancellationToken.None);

// Read
Role fetched = await roleRepository.GetRoleById(role.Id, CancellationToken.None);

// Update
fetched.RoleName = "content-moderator";
bool updated = await roleRepository.UpdateRole(fetched, CancellationToken.None);

// Soft-delete (sets Status = Disabled)
bool deleted = await roleRepository.DeleteRole(role.Id, CancellationToken.None);

Token Validation

bool isValid = await manageTokenService.ValidateRefreshToken(
    userDto.RefreshToken,
    CancellationToken.None);

Returns true only when the token exists in the database, has not been revoked, and has not expired.


HTTP Endpoints

The built-in UserController exposes the following routes under /user:

Method Route Auth Required Description
POST /user/login No Authenticate and receive tokens
POST /user/register No Register a new user
GET /user/verify No Verify email with 6-digit code
PUT /user/status Admin JWT Change the role of a user
PUT /user/password Admin JWT Change the password of a user
DELETE /user Admin JWT Soft-delete a user

All endpoints return 400 Bad Request with a list of UserValidationError objects when input validation fails.


Project Structure

UserLibrary/
├── Configuration/
│   └── AppConfiguration.cs         # Settings POCO (connection string, JWT params, AdminEmail)
├── Controller/
│   └── UserController.cs           # Built-in ASP.NET Core API controller
├── Dto/
│   ├── UserDto.cs                  # User data transfer object (includes VerificationCode, IsVerified)
│   └── RoleDto.cs                  # Role data transfer object
├── Enumeration/
│   └── UserEnumeration.cs          # Typed validation error codes (UserValidationError)
├── Extentions/
│   └── UserMappingExtentions.cs    # ToDto / ToEntity mapping extensions
├── Interface/
│   ├── IUserRepository.cs
│   ├── IRoleRepository.cs
│   ├── IRefreshTokenRepository.cs
│   ├── IUserService.cs
│   ├── IManageTokenService.cs
│   └── IDatabaseInitializer.cs
├── Model/
│   ├── User.cs                     # Includes VerificationCode, IsVerified
│   ├── Role.cs
│   ├── AccessToken.cs
│   ├── RefreshToken.cs
│   └── Status.cs                   # Enum: Enabled = 0, Disabled = 1
├── Repository/
│   ├── UserRepository.cs           # Includes VerifyUser, GetUserNotVerified
│   ├── RoleRepository.cs
│   └── RefreshTokenRepository.cs
├── Request/
│   └── Request.cs                  # LoginRequest, RegisterRequest, VerifyUserRequest, …
├── Response/
│   └── Response.cs                 # LoginResponse, RegisterResponse, VerifyUserResponse, …
├── Service/
│   ├── UserService.cs
│   └── ManageTokenService.cs
├── Sql/
│   └── DatabaseInitializer.cs      # Auto-creates DB, tables, and seeds roles
├── Statics/
│   ├── AddUserServices.cs          # AddUserLibrary() extension method
│   ├── GenerateVerificationCode.cs # Cryptographically-random 6-digit code generator
│   └── TokenHelper.cs             # JWT generation utility
└── Validator/
    └── UserValidator.cs            # Static validators for all request types

Models & DTOs

User / UserDto

Property Type Notes
Id Guid Auto-generated on registration
FirstName string
LastName string
Email string Used as login identifier
Password string BCrypt hashed; null in responses
BirthDate DateTime
Status Status Enabled or Disabled
VerificationCode int? 6-digit code; null after verification
IsVerified bool false until VerifyEmail is called
Role Role/RoleDto Auto-assigned based on AdminEmail config
AccessToken AccessToken Populated after login
RefreshToken RefreshToken Populated after login

AccessToken

Property Type Notes
Token string JWT string
Expiration DateTime UTC, 15 minutes from issue

RefreshToken

Property Type Notes
Id Guid
UserId Guid
Token string GUID-based opaque string
Expiration DateTime UTC, 7 days from issue
IsRevoked bool

Requests & Responses

Requests

Class Fields
LoginRequest email, password
RegisterRequest email, password, firstname, lastname, birthdate
VerifyUserRequest email, verificationCode
UpdateUserStatusRequest email, role
UpdateUserPasswordRequest email, password
DeleteUserRequest email

Responses

Class Fields
LoginResponse email, token (AccessToken), refreshToken
RegisterResponse email, rolename, firstname, lastname, birthdate
VerifyUserResponse email, isVerified
UpdateUserStatusResponse email, role
UpdateUserPasswordResponse email, password
DeleteUserResponse email, status

Interfaces

Interface Responsibility
IUserRepository CRUD + verify + list-unverified on Users table
IRoleRepository CRUD operations on the Role table
IRefreshTokenRepository Persist and validate refresh tokens
IUserService High-level register / verify / login / update / delete flow
IManageTokenService Generate and validate access & refresh tokens
IDatabaseInitializer Create database schema and seed initial data

Validation Errors

All validation errors are returned as a list of UserValidationError objects with a Code and Message.

Code Description
EMAIL_REQUIRED Email field is missing
INVALID_EMAIL_FORMAT Email does not match username@example.com pattern
EMAIL_NOT_FOUND No user found with the given email
PASSWORD_REQUIRED Password field is missing
PASSWORD_TOO_SHORT Password is shorter than the minimum length
INVALID_PASSWORD_FORMAT Password must be ≥ 8 chars with uppercase, lowercase, digit, and symbol
FIRSTNAME_REQUIRED First name is missing
FIRSTNAME_TOO_LONG First name exceeds maximum length
LASTNAME_REQUIRED Last name is missing
LASTNAME_TOO_LONG Last name exceeds maximum length
BIRTHDATE_REQUIRED Birth date is missing
INVALID_BIRTHDATE Birth date is not a valid date
ROLE_REQUIRED Role field is missing
INVALID_ROLE Role must be admin or user
INVALID_CODE_FORMAT Verification code must be exactly 6 digits
INVALID_USER_ID Request body is null or malformed

Dependencies

Package Version Notes
BCrypt.Net-Next 4.1.0 Password hashing
Microsoft.Data.SqlClient 7.0.0 SQL Server connectivity
Newtonsoft.Json 13.0.4 JSON serialization
Microsoft.Extensions.Options 10.0.5 Options pattern
Microsoft.Extensions.Options.ConfigurationExtensions 10.0.5 Config binding
Microsoft.Extensions.Configuration.Binder 10.0.5 Configuration binding
Microsoft.AspNetCore.App (framework ref) ASP.NET Core integration

License

This project is licensed under the MIT License — see LICENSE for details.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.3 113 3/27/2026
1.2.2 97 3/26/2026
1.2.1 96 3/25/2026
1.2.0 95 3/25/2026
1.1.3 93 3/25/2026
1.1.2 97 3/24/2026
1.1.1 94 3/24/2026
1.1.0 100 3/24/2026
1.0.10 112 3/24/2026
1.0.9 94 3/23/2026
1.0.7 95 3/23/2026
1.0.6 96 3/23/2026
1.0.5 98 3/23/2026
1.0.4 93 3/23/2026
1.0.3 99 3/23/2026
1.0.2 97 3/23/2026
1.0.1 98 3/23/2026
1.0.0 96 3/23/2026