https://github.com/ja7ad/otp
A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords β RFC 4226, RFC 6238 and RFC 6287 compliant.
https://github.com/ja7ad/otp
2fa authentication go golang hotp mfa otp rfc4226 rfc6238 rfc6287 security totp
Last synced: about 1 month ago
JSON representation
A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords β RFC 4226, RFC 6238 and RFC 6287 compliant.
- Host: GitHub
- URL: https://github.com/ja7ad/otp
- Owner: ja7ad
- License: mit
- Created: 2025-04-03T05:42:36.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2025-05-11T17:57:18.000Z (5 months ago)
- Last Synced: 2025-08-01T00:30:42.268Z (2 months ago)
- Topics: 2fa, authentication, go, golang, hotp, mfa, otp, rfc4226, rfc6238, rfc6287, security, totp
- Language: Go
- Homepage:
- Size: 2.99 MB
- Stars: 34
- Watchers: 2
- Forks: 1
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README

[](https://codecov.io/gh/Ja7ad/otp)
[](https://goreportcard.com/report/github.com/ja7ad/otp)
[](https://pkg.go.dev/github.com/ja7ad/otp)# π OTP
A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords β RFC [4226](https://datatracker.ietf.org/doc/html/rfc4226), RFC [6238](https://datatracker.ietf.org/doc/html/rfc6238) and RFC [6287](https://datatracker.ietf.org/doc/html/rfc6287) compliant.
- [Feature](#-features)
- [Installation](#-installation-go--124)
- [Library](#-using-go)
- [OTP-API](#-prebuilt-binary)
- [Docker](#-docker-image)
- [Comparison](#-comparison)
- [Performance](#-performance-comparison)
- [Features](#-feature-comparison)
- [Proof algorithm](#-algorithm-rfc)
- [Usage](#-usage)
- [Contributing](#-contributing)
- [Reference](#-references)## β¨ Features
- Zero dependencies β fully self-contained, no external packages
- High performance with low allocations
- Supports HOTP (RFC [4226](https://datatracker.ietf.org/doc/html/rfc4226)), TOTP (RFC [6238](https://datatracker.ietf.org/doc/html/rfc6238)) and OCRA (RFC [6287](https://datatracker.ietf.org/doc/html/rfc6287)) algorithms
- Configurable OTP digit lengths: 6, 8, or 10
- Supports SHA1, SHA256, and SHA512 HMAC algorithms
- Constant-time OTP validation to prevent timing attacks
- Clock skew tolerance for TOTP validation
- Generates `otpauth://` URLs for Google Authenticator and compatible apps
- Parses `otpauth://` URLs into configuration structs
- Secure random secret generation (base32 encoded)
- Thoroughly tested against official RFC test vectors
- Includes fuzz tests, benchmark coverage, and solid algorithm validationHereβs your updated `README.md` **Installation** section with release and Docker image info:
## π¦ Installation (Go >= 1.24)
### π οΈ Using Go
```bash
go get -u github.com/ja7ad/otp
```> Node.js bindings are available [here](./otp-js).
---
### π Prebuilt Binary
Download the latest CLI/API binary for your platform from the [latest release page](https://github.com/ja7ad/otp/releases/latest).
> Online demo: https://otp-api.leapcell.app/docs
```shell
$ otp -serve localhost:8080
2025/04/06 10:41:48 INFO starting server address=:8080
2025/04/06 10:41:50 INFO request method=GET path=/docs/index.html status=200 duration=740.394Β΅s
2025/04/06 10:41:51 INFO request method=GET path=/docs/doc.json status=200 duration=803.67Β΅s
2025/04/06 10:41:53 INFO request method=GET path=/ status=200 duration=149.042Β΅s
2025/04/06 10:41:54 INFO request method=GET path=/docs status=302 duration=24.444Β΅s
```| Method | Path | Description |
|--------|--------------------|----------------------------------|
| POST | `/totp/generate` | Generate a TOTP code |
| POST | `/totp/validate` | Validate a TOTP code |
| POST | `/hotp/generate` | Generate a HOTP code |
| POST | `/hotp/validate` | Validate a HOTP code |
| POST | `/ocra/generate` | Generate an OCRA code |
| POST | `/ocra/validate` | Validate an OCRA code |
| GET | `/otp/secret` | Generate a random base32 secret |
| POST | `/otp/url` | Generate otpauth URL |
| GET | `/ocra/suites` | List supported OCRA suites |
| POST | `/ocra/suite` | Parse and describe suite config |---
### π³ Docker Image
You can also run the server using Docker:
```bash
docker pull ja7adr/otp
docker run -p 8080:8080 ja7adr/otp
```> Image available at [Docker Hub](https://hub.docker.com/r/ja7adr/otp)
## π¬ Comparison
This comparison is performance and feature.
#### π Performance Comparison
This comparison is for `Ja7ad/otp` vs `pquerna/otp`
| Algorithm | Suite | Digits | Library | `ns/op` | `B/op` | `allocs/op` | `N` (runs/sec) |
|-----------|----------------------------------------|--------|----------------|---------|--------|--------------|----------------|
| SHA1 | `OCRA-1:HOTP-SHA1-6:QN08` | 6 | **Ja7ad/otp** | **1134** | **552** | **9** | **881,058** |
| SHA1 | HOTP/TOTP (default) | 6 | pquerna/otp | 1420 | 592 | 13 | 704,225 |
| SHA256 | `OCRA-1:HOTP-SHA256-8:C-QN08-PSHA1` | 8 | **Ja7ad/otp** | **984.3** | **592** | **9** | **1,015,907** |
| SHA256 | HOTP/TOTP (default) | 8 | pquerna/otp | 1477 | 728 | 13 | 677,236 |
| SHA512 | `OCRA-1:HOTP-SHA512-8:QN08-T1M` | 8 | **Ja7ad/otp** | **1752** | **944** | **9** | **570,853** |
| SHA512 | HOTP/TOTP (default) | 8 | pquerna/otp | 2359 | 1224 | 13 | 423,778 || Metric | Ja7ad/otp | pquerna/otp | β Winner |
|------------------|---------------------|---------------------|----------|
| **Execution time** (`ns/op`) | **~2x faster** across all algorithms and digit sizes | Slower in all cases | β **Ja7ad/otp** |
| **Memory usage** (`B/op`) | **~30β50% less** memory allocated | Higher allocations | β **Ja7ad/otp** |
| **Allocations** (`allocs/op`) | **7** allocations | **13** allocations | β **Ja7ad/otp** |
| **Dependencies** | **Zero** external deps | Relies on stdlib + extras | β **Ja7ad/otp** |- `Ja7ad/otp`: **736 ns**, **520 B**, **7 allocs**
- `pquerna/otp`: **1495 ns**, **728 B**, **13 allocs**#### β Feature Comparison
| Feature | Ja7ad/otp | pquerna/otp |
|-----------------------------|-----------|-------------|
| RFC 4226 HOTP | β | β |
| RFC 6238 TOTP | β | β |
| RFC 6287 OCRA | β | β |
| Built-in OCRA Suite Configs | β | β |
| Full RFC Test Vector Suite | β | β |
| Constant-Time Validation | β | β |
| Cross-platform Friendly | β | β |
| Zero Dependency Core | β | β (uses crypto/rand + external parsing) |## π Algorithm (RFC)
- [RFC 4226 / 6238](docs/rfc4226.md) proof algorithm
- [RFC 6287](docs/rfc6287.md) proof algorithm## π Usage
TOTP example
```go
package mainimport (
"fmt"
"github.com/ja7ad/otp"
"log"
"time"
)func main() {
secret, err := otp.RandomSecret(otp.SHA1)
if err != nil {
log.Fatal(err)
}t := time.Now()
code, err := otp.GenerateTOTP(secret, t, otp.DefaultTOTPParam)
if err != nil {
log.Fatal(err)
}fmt.Println(code)
ok, err := otp.ValidateTOTP(secret, code, t, otp.DefaultTOTPParam)
if err != nil {
log.Fatal(err)
}if !ok {
log.Fatal("Invalid OTP")
}url, err := otp.GenerateTOTPURL(otp.URLParam{
Issuer: "https://example.com",
Secret: secret,
AccountName: "foobar",
Period: otp.DefaultTOTPParam.Period,
Digits: otp.DefaultTOTPParam.Digits,
Algorithm: otp.DefaultTOTPParam.Algorithm,
})
if err != nil {
log.Fatal(err)
}fmt.Println(url.String())
}
```HOTP example code
```go
package mainimport (
"fmt"
"github.com/ja7ad/otp"
"log"
)func main() {
secret, err := otp.RandomSecret(otp.SHA1)
if err != nil {
log.Fatal(err)
}counter := uint64(1)
code, err := otp.GenerateHOTP(secret, counter, otp.DefaultHOTPParam)
if err != nil {
log.Fatal(err)
}fmt.Println(code)
ok, err := otp.ValidateHOTP(secret, code, counter, otp.DefaultHOTPParam)
if err != nil {
log.Fatal(err)
}if !ok {
log.Fatal("Invalid OTP")
}url, err := otp.GenerateHOTPURL(otp.URLParam{
Issuer: "https://example.com",
Secret: secret,
AccountName: "foobar",
Period: otp.DefaultHOTPParam.Period,
Digits: otp.DefaultHOTPParam.Digits,
Algorithm: otp.DefaultHOTPParam.Algorithm,
})
if err != nil {
log.Fatal(err)
}fmt.Println(url.String())
}
```OCRA example code
```go
package mainimport (
"fmt"
"github.com/ja7ad/otp"
)func main() {
secret, err := otp.RandomSecret(otp.SHA1)
if err != nil {
panic(err)
}suite := otp.MustRawSuite("OCRA-1:HOTP-SHA1-6:QN08")
code, err := otp.GenerateOCRA(secret, suite, otp.OCRAInput{
Challenge: []byte("12345678"),
})if err != nil {
panic(err)
}ok, err := otp.ValidateOCRA(secret, code, suite, otp.OCRAInput{
Challenge: []byte("12345678"),
})
if err != nil {
panic(err)
}fmt.Println(ok)
}
```## π€ Contributing
We welcome contributions of all kinds β from fixing bugs and improving documentation to implementing new RFCs.
Please read our [Contributing Guide](CONTRIBUTING.md) to get started. It includes setup instructions, coding standards, and development workflows.
Whether you're filing an issue, submitting a pull request, or suggesting an improvement β thank you for helping make this library better! π
## π References
- [RFC 6287 - OCRA](https://datatracker.ietf.org/doc/html/rfc6287)
- [RFC 4226 - HOTP](https://datatracker.ietf.org/doc/html/rfc4226)
- [RFC 6238 - TOTP](https://datatracker.ietf.org/doc/html/rfc6238)