GM.OTP.Persistence
1.1.0
dotnet add package GM.OTP.Persistence --version 1.1.0
NuGet\Install-Package GM.OTP.Persistence -Version 1.1.0
<PackageReference Include="GM.OTP.Persistence" Version="1.1.0" />
<PackageVersion Include="GM.OTP.Persistence" Version="1.1.0" />
<PackageReference Include="GM.OTP.Persistence" />
paket add GM.OTP.Persistence --version 1.1.0
#r "nuget: GM.OTP.Persistence, 1.1.0"
#:package GM.OTP.Persistence@1.1.0
#addin nuget:?package=GM.OTP.Persistence&version=1.1.0
#tool nuget:?package=GM.OTP.Persistence&version=1.1.0
<p align="center"> <img src="https://raw.githubusercontent.com/gmetskhvarishvili/GM.OTP/master/icon.png" alt="GM.OTP" width="140" height="140" /> </p>
GM.OTP
A lightweight, storage-agnostic one-time password (OTP) core for .NET. OtpManager generates and
verifies codes over pluggable code-generation, hashing, and clock abstractions — binding each code to
its subject and destination so a tampered row can't be verified. Part of the GM.* family of
packages. Targets net10.0.
Packages
This repository ships three packages that version and release in lockstep:
| Package | What it provides |
|---|---|
GM.OTP |
OtpManager (generate/verify), options, request/result models. |
GM.OTP.Domain |
The OtpChallenge aggregate and the ICodeGenerator / ICodeHasher / IDateTimeProvider abstractions. |
GM.OTP.Persistence |
The EF Core configuration for OtpChallenge. |
Install
dotnet add package GM.OTP
Use
Provide implementations of the three abstractions (how you generate codes, hash them, and read the clock), then generate and verify:
var manager = new OtpManager(codeGenerator, codeHasher, dateTimeProvider, new OtpOptions
{
CodeLength = 6,
ExpirationMinutes = 2,
MaxAttempts = 5,
});
// Issue a code for a subject (e.g. a user) + destination (e.g. an email/phone).
OtpGenerationData data = manager.Generate(subject: "user-42", destination: "user@example.com");
// Persist a challenge built from `data` (hash, salt, expiry, max attempts), then send `data.PlainCode`.
var challenge = new OtpChallenge(
"user-42", "user@example.com", data.CodeHash, data.Salt,
OtpPurposes.Login, data.ExpiresAtUtc, data.MaxAttempts);
// Later, verify the code the user entered:
VerifyOtpResult result = manager.Verify(challenge, "123456");
if (result.IsValid) { /* proceed */ }
How it protects codes
- Salted, subject/destination-bound hashes — the stored hash covers
code + subject + destination, so verification fails if any of those columns were altered. - Single use — a verified challenge is marked used and can't be replayed.
- Attempt limiting — each failed attempt is counted; exceeding
MaxAttemptsthrowsOtpMaxAttemptsExceededException. - Expiry — codes expire after
ExpirationMinutes.
Contributing & releases
Versioning is automated from Conventional Commits — see CONTRIBUTING.md. All three packages release together under one version.
License
MIT — see LICENSE.
| 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
- GM.EntityFramework.Persistence (>= 1.2.1)
- GM.OTP.Domain (>= 1.1.0)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.3)
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.1.0 | 108 | 8/1/2026 |