{"id":19880635,"url":"https://github.com/emzi0767/ezotp","last_synced_at":"2025-05-02T13:32:20.329Z","repository":{"id":124526373,"uuid":"293162784","full_name":"Emzi0767/EzOTP","owner":"Emzi0767","description":"A simple, easy-to-use one-time password generator implementation for .NET Core 3.1. Compatible with Google Authenticator.","archived":false,"fork":false,"pushed_at":"2020-09-30T21:51:32.000Z","size":63,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-29T06:48:39.910Z","etag":null,"topics":["2fa","2factor","google-authenticator","hotp","hotp-generator","mfa","multifactor-authentication","one-time-password","one-time-passwords","otp","otpauth","totp","totp-generator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Emzi0767.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.TXT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"patreon":"emzi0767","ko_fi":"emzi0767","custom":["https://paypal.me/Emzi0767/5USD"]}},"created_at":"2020-09-05T22:18:15.000Z","updated_at":"2024-09-11T13:31:44.000Z","dependencies_parsed_at":"2023-08-18T22:02:50.274Z","dependency_job_id":null,"html_url":"https://github.com/Emzi0767/EzOTP","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FEzOTP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FEzOTP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FEzOTP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Emzi0767%2FEzOTP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Emzi0767","download_url":"https://codeload.github.com/Emzi0767/EzOTP/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252046269,"owners_count":21685983,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2fa","2factor","google-authenticator","hotp","hotp-generator","mfa","multifactor-authentication","one-time-password","one-time-passwords","otp","otpauth","totp","totp-generator"],"created_at":"2024-11-12T17:12:04.779Z","updated_at":"2025-05-02T13:32:20.322Z","avatar_url":"https://github.com/Emzi0767.png","language":"C#","funding_links":["https://patreon.com/emzi0767","https://ko-fi.com/emzi0767","https://paypal.me/Emzi0767/5USD"],"categories":[],"sub_categories":[],"readme":"# EzOTP\nA simple, lightweight, and easy to use implementation of One-Time Password protocol, both time-based and counter-based \nvariants.\n\n## Installation\nTo start using the package, just [install it from NuGet](https://www.nuget.org/packages/EzOTP/).\n\n## Usage\nThe library is designed to be fairly straightforward and easy to use.\n\n### Basic usage\nThe primary use case for the library is usage from QR codes, or other media containing [OTP authentication](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) \nURIs (that is, URIs with `otpauth` scheme).\n\n```cs\nvar otp = OtpGenerator.ParseUri(\"otpauth://totp/ACME%20Co:john@example.com?secret=DGW24UIKQZBELXEMY64PICAL5IGYMJM6\u0026issuer=ACME%20Co\u0026algorithm=SHA1\u0026digits=6\u0026period=30\");\nvar code = otp.GenerateCode();\n```\n\nThis will generate a code in `XXXXXX` format, as a string.\n\n### Generate raw code\nIn the event a numeric code is more desired (e.g. for comparison purposes), one can use `GenerateRaw()` method instead. \nIt returns an integer with appropriate number of digits.\n\n```cs\nvar otp = OtpGenerator.ParseUri(\"otpauth://totp/ACME%20Co:john@example.com?secret=DGW24UIKQZBELXEMY64PICAL5IGYMJM6\u0026issuer=ACME%20Co\u0026algorithm=SHA1\u0026digits=6\u0026period=30\");\nvar code = otp.GenerateRaw();\n```\n\n### Groupping digits\n`groupSize` argument of `GenerateCode()` defines a maximum number of digits in a single group. For example, if the \ngenerator is configured for generating 6 digit-long codes, the output looks like so, depending on the value of \n`groupSize` argument:\n- 1: `X X X X X X`\n- 2: `XX XX XX`\n- 3: `XXX XXX`\n- 4: `XX XXXX`\n- 5: `X XXXXX`\n- 6+: `XXXXXX`\n\n### Handling desynchronization\nDesynchronization between 2 generators can occur. Because of this, EzOTP provides 2 ways of handling \npotentially-desynchronized generators.\n\n#### Method 1: Offset\nIf the desynchronization offset of both counters is known, one can use `offset` argument of `GenerateCode()` method. \nThis will add a fixed offset to generated counter values, without changing the counter value itself.\n\nFor example, using Google Authenticator-compatible examble from **Basic usage** section, let's assume the \ndesynchronization between the two devices is between 60 and 90 seconds, such that current application is lagging.\n\nSince Google Authenticator uses periods of 30 seconds, this means we have to add `60 / 30 = 2` periods to the counter, \nmeaning we need to use an offset of 2:\n\n```cs\nvar otp = OtpGenerator.ParseUri(\"otpauth://totp/ACME%20Co:john@example.com?secret=DGW24UIKQZBELXEMY64PICAL5IGYMJM6\u0026issuer=ACME%20Co\u0026algorithm=SHA1\u0026digits=6\u0026period=30\");\nvar code = otp.GenerateCode(offset: 2);\n```\n\nShould the current application be the one ahead, an offset of -2 would be applied instead.\n\n#### Method 2: Counter window\nFor authenticating, RFC actually recommends testing several codes in the past and future. However generating several \ncodes using above methods is not the best choice, as it does trip the counter internally, meaning that if the generator \ntype uses a counter (that is, is a HOTP generator), it will increase the internal counter value for every code \ngenerated.\n\nTo alleviate this issue, EzOTP provides methods for generating a \"window\" of codes, whilst incrementing the counter \nonly once.\n\nAssuming that user-provided code is in an `int` variable called `userCode`, one can validate their input like so:\n\n```cs\nvar otp = OtpGenerator.ParseUri(\"otpauth://totp/ACME%20Co:john@example.com?secret=DGW24UIKQZBELXEMY64PICAL5IGYMJM6\u0026issuer=ACME%20Co\u0026algorithm=SHA1\u0026digits=6\u0026period=30\");\nif (!otp.GenerateRawWindow(window: 1).Contains(userCode))\n    // fail the authentication process\n```\n\nThe method optionally takes a `window` argument, which defines the window size. Given a window size of `n`, EzOTP will \ngenerate `n` codes before current counter value, 1 current counter value code, and `n` codes after current counter \nvalue.\n\nSpecifying a value of `0` or a negative number will use default window sizes. For TOTP it's 1, for HOTP it's 2.\n\n### OTP generator configuration\nAn OTP generator can be constructed without parsing an URI, by constructing an instance of `OtpGeneratorConfiguration` \ndirectly. It's an abstract class, which serves as a common base for 2 configuration types:\n- `TotpGeneratorConfiguration`: Contains TOTP-specific properties.\n- `HotpGeneratorConfiguration`: Contains HOTP-specific properties.\n\nThe configuration instance can then be supplied to the constructor of `OtpGenerator`. This allows for storing OTP \ngenerator state and restoring it without having to use URIs. This is useful for HOTP particularly, where the counter \nvalue has to be re-recorded after every generation.\n\n### Generating Google Authenticator URIs\nOne can easily generate configurations for Google Authenticator, by using `GenerateGoogleAuthenticator()` static method \non either `TotpGeneratorConfiguration` or `HotpGeneratorConfiguration` classes.\n\nThese methods return configuration objects, that can then be plugged into `OtpGenerator` constructor, or just returned \nas an URI to a user.\n\n## Support me\nLots of effort went into making this software.\n\nIf you feel like I'm doing a good job, or just want to throw money at me, you can do so through any of the following:\n- [Patreon](https://patreon.com/emzi0767)\n- [Ko-Fi](https://ko-fi.com/emzi0767)\n- [PayPal](https://paypal.me/Emzi0767/5USD)\n\n## Other questions\nIf you have other questions or would like to talk in general, feel free to visit my Discord server.\n\n[![Emzi's Central Dispatch](https://discordapp.com/api/guilds/207879549394878464/embed.png?style=banner1)](https://discord.gg/rGKrJDR)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femzi0767%2Fezotp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femzi0767%2Fezotp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femzi0767%2Fezotp/lists"}