Astrolabe.LocalUsers 4.0.0

dotnet add package Astrolabe.LocalUsers --version 4.0.0
                    
NuGet\Install-Package Astrolabe.LocalUsers -Version 4.0.0
                    
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="Astrolabe.LocalUsers" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Astrolabe.LocalUsers" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Astrolabe.LocalUsers" />
                    
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 Astrolabe.LocalUsers --version 4.0.0
                    
#r "nuget: Astrolabe.LocalUsers, 4.0.0"
                    
#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.
#addin nuget:?package=Astrolabe.LocalUsers&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Astrolabe.LocalUsers&version=4.0.0
                    
Install as a Cake Tool

Astrolabe.LocalUsers

NuGet License: MIT

A comprehensive library for implementing local user management in .NET applications. Part of the Astrolabe Apps library stack.

Overview

Astrolabe.LocalUsers provides abstractions and base classes for implementing robust user management, including:

  • Account creation with email verification
  • Secure password authentication
  • Multi-factor authentication (MFA) via SMS/phone
  • Password reset functionality
  • User profile management (email and MFA number changes)

Installation

dotnet add package Astrolabe.LocalUsers

Features

User Management

  • Account Creation: Create new user accounts with email verification
  • Authentication: Authenticate users with username/password
  • Multi-Factor Authentication: MFA support using verification codes
  • Password Management: Change and reset password flows
  • Profile Management: Update email and MFA phone number

Security Features

  • Password Hashing: Secure password storage with salted SHA-256 hashing (customizable)
  • Validation: Comprehensive validation for user inputs
  • MFA Support: Two-factor authentication with verification codes

Getting Started

Step 1: Define Your User Model

Create a class that implements ICreateNewUser:

public class NewUser : ICreateNewUser
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string Confirm { get; set; }
    
    // Additional properties as needed
}

Step 2: Create a User ID Type

Define your user ID type (typically int, Guid, or string).

Step 3: Implement a User Service

Extend the AbstractLocalUserService<TNewUser, TUserId> class:

public class MyUserService : AbstractLocalUserService<NewUser, int>
{
    public MyUserService(IPasswordHasher passwordHasher, LocalUserMessages? messages = null) 
        : base(passwordHasher, messages)
    {
    }

    // Implement abstract methods (see implementation section below)
}

Step 4: Set Up a Controller

Extend the AbstractLocalUserController<TNewUser, TUserId> class:

[ApiController]
[Route("api/users")]
public class UserController : AbstractLocalUserController<NewUser, int>
{
    public UserController(ILocalUserService<NewUser, int> userService) 
        : base(userService)
    {
    }

    protected override int GetUserId()
    {
        // Extract user ID from the current authenticated user's claims
        return int.Parse(User.FindFirst("userId")?.Value ?? "0");
    }
}

Implementation Guide

Implementing AbstractLocalUserService

You must implement these abstract methods:

// Send verification email with code
protected abstract Task SendVerificationEmail(TNewUser newUser, string verificationCode);

// Create unverified account in your database
protected abstract Task CreateUnverifiedAccount(TNewUser newUser, string hashedPassword, string verificationCode);

// Count existing users with the same email
protected abstract Task<int> CountExistingForEmail(string email);

// Verify email with code and return JWT token
protected abstract Task<string?> VerifyAccountCode(string code);

// MFA verification for account
protected abstract Task<string?> MfaVerifyAccountForUserId(MfaAuthenticateRequest mfaAuthenticateRequest);

// Authenticate with username/password and return JWT token
protected abstract Task<string?> AuthenticatedHashed(AuthenticateRequest authenticateRequest, string hashedPassword);

// Send an MFA code via SMS/phone
protected abstract Task<bool> SendCode(MfaCodeRequest mfaCodeRequest);
protected abstract Task<bool> SendCode(TUserId userId, string? number=null);

// Verify MFA code and return JWT token
protected abstract Task<string?> VerifyMfaCode(string token, string code, string? number);
protected abstract Task<bool> VerifyMfaCode(TUserId userId, string code, string? number);

// Set reset code for password reset
protected abstract Task SetResetCodeAndEmail(string email, string resetCode);

// Change email for a user
protected abstract Task<bool> EmailChangeForUserId(TUserId userId, string hashedPassword, string newEmail);

// Change password with appropriate methods
protected abstract Task<(bool, Func<string, Task<string>>?)> PasswordChangeForUserId(TUserId userId, string hashedPassword);
protected abstract Task<Func<string, Task<string>>?> PasswordChangeForResetCode(string resetCode);

// Change MFA phone number
protected abstract Task<bool> ChangeMfaNumberForUserId(TUserId userId, string hashedPassword, string newNumber);

Customization

Custom Password Requirements

Override the ApplyPasswordRules method in your service implementation:

protected override void ApplyPasswordRules<T>(AbstractValidator<T> validator)
{
    // Default is 8 character minimum
    validator.RuleFor(x => x.Password).MinimumLength(10);
    validator.RuleFor(x => x.Password).Must(HasUpperCase).WithMessage("Password must contain an uppercase letter");
    validator.RuleFor(x => x.Password).Must(HasNumber).WithMessage("Password must contain a number");
}

private bool HasUpperCase(string password) => password.Any(char.IsUpper);
private bool HasNumber(string password) => password.Any(char.IsDigit);

Custom Error Messages

Provide a custom LocalUserMessages instance:

var messages = new LocalUserMessages
{
    AccountExists = "This email is already registered",
    PasswordMismatch = "Passwords do not match",
    PasswordWrong = "The password you entered is incorrect",
    EmailInvalid = "Please enter a valid email address"
};

var userService = new MyUserService(passwordHasher, messages);

Custom Email Validation Rules

Override the ApplyCreationRules method:

protected override Task ApplyCreationRules(NewUser user, AbstractValidator<NewUser> validator)
{
    // Add domain-specific validation
    validator.RuleFor(x => x.Email).Must(x => x.EndsWith("@mycompany.com"))
        .WithMessage("Only company email addresses are allowed");
    
    return Task.CompletedTask;
}

Secure Password Storage

The library includes a SaltedSha256PasswordHasher implementation, but you can create your own by implementing the IPasswordHasher interface:

public class BcryptPasswordHasher : IPasswordHasher
{
    public string Hash(string password)
    {
        return BCrypt.Net.BCrypt.HashPassword(password);
    }
}

API Endpoints

The controller provides these standard endpoints:

  • POST /create - Create a new account
  • POST /verify - Verify account with email code
  • POST /mfaVerify - Verify account with MFA
  • POST /authenticate - Authenticate with username/password
  • POST /mfaCode/authenticate - Send MFA code
  • POST /mfaAuthenticate - Authenticate with MFA code
  • POST /forgotPassword - Initiate password reset
  • POST /changeEmail - Change email address
  • POST /changeMfaNumber - Change MFA phone number
  • POST /mfaChangeMfaNumber - Change MFA number with verification
  • POST /changePassword - Change password (authenticated)
  • POST /resetPassword - Reset password (with reset code)
  • POST /mfaCode/number - Send MFA code to a specific number

License

MIT

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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
4.0.0 192 5/29/2025
3.0.0 209 10/7/2024
2.0.0 210 3/21/2024
1.0.0 196 1/28/2024