twofa

package module
v0.10.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2024 License: BSD-3-Clause Imports: 21 Imported by: 0

README

Fiber 2FA Middleware

Go Version Go Reference Go Report Card Coverage Status

This is a custom 2FA (Two-Factor Authentication) middleware for the Fiber web framework. It provides a secure and easy-to-use solution for implementing 2FA in Fiber applications. The middleware supports both HOTP (HMAC-based One-Time Password) and TOTP (Time-based One-Time Password) authentication and offers customizable configuration options.

[!WARNING] This 2FA middleware is still a work in progress and may not be stable for use in production environments (e.g., QR Code Builder), since it is rewritten from scratch with some improvements. Use it with caution and thoroughly test it before deploying to production. It is recommended to use it locally for testing purposes.

[!NOTE] This 2FA project was inspired by some QR code systems in my country (e.g., https://qris.id/). However, it is built in a modern way and purely written in Go (which is more secure and can leverage the system easily), rather than the traditional way (where it is written in PHP). More QR code system projects might be implemented in the future (e.g., payment systems through banking similar to https://qris.id/).

Features

The middleware provides the following features:

HOTP Authentication (Currently implemented internally)
  • Generation and verification of HOTP tokens
  • Customizable token length and counter synchronization
  • Automatic generation of random secrets if not provided
  • Support for various hash algorithms (SHA1, SHA256, SHA512, BLAKE2b, BLAKE3)
  • Configurable synchronization window for handling counter desynchronization
  • Automatic counter resynchronization based on predefined thresholds
  • Dynamic adjustment of synchronization window size based on counter mismatch frequency

[!NOTE] Some HOTP implementations here follow the standards defined in RFC 4226. However, they are built on top of modern & advanced cryptographic knowledge and best practices, rather than using outdated or traditional approaches. This is because Go is considered one of the best programming languages for cryptographic implementations, compared to C or C++.

TOTP Authentication
  • Generation and verification of TOTP tokens
  • Customizable token length and time step size
  • Automatic generation of random secrets if not provided
  • Support for various hash algorithms (SHA1, SHA256, SHA512, BLAKE2b, BLAKE3)
  • Configurable synchronization window for handling time drift
  • Automatic token expiration and cleanup

[!NOTE] Some TOTP implementations here adhere to the standards defined in RFC 6238. However, they are built on top of modern & advanced cryptographic knowledge and best practices, rather than relying on outdated or traditional methods. This is because Go is considered one of the best programming languages for cryptographic implementations, compared to C or C++.

The notes effectively communicate that the HOTP and TOTP implementations follow the respective RFCs while leveraging modern and advanced cryptographic knowledge and best practices. The emphasis on Go's suitability for cryptographic implementations compared to C or C++ is also clearly stated.

Flexible Storage
  • Support for various storage providers (e.g., in-memory, MongoDB, MySQL, PostgreSQL, Redis, SQLite3)
  • Customizable storage configuration
  • Secure cookie-based authentication for 2FA validation
  • Customizable cookie settings (name, expiration, domain, etc.)
Customizable Redirect
  • Configurable redirect URL for 2FA validation
  • Ability to skip 2FA for specific paths
JSON Marshaling and Unmarshaling
  • Customizable JSON marshaling and unmarshaling functions
  • Support for custom JSON encoding/decoding
Advanced Configuration
  • Customizable context key for storing 2FA information
  • Ability to skip middleware based on custom logic
QR Code Generation
  • Automatic generation of QR code images for 2FA secret keys
  • Customizable QR code path template
  • Support for custom QR code images
  • Customizable QR code content template
  • Configurable QR code recovery level and image size
Customizable Token Lookup
  • Flexible token lookup from various sources (header, query, form, param, cookie)
  • Configurable token lookup string format
Context Key Management
  • Customizable context key for storing 2FA information in the request context
  • Ability to retrieve and manage context keys based on account names
Error Handling
  • Customizable error handling for unauthorized and internal server errors
  • Support for various response formats (plain text, JSON, XML)
Built-in 64-bit Integer
  • Most bits and Unix timestamps are built using 64-bit integers, capable of representing dates billions of years into the future.

More features and validation capabilities will be added in the future to enhance the middleware's functionality and cater to a wider range of validation scenarios.

Benchmark

  • v0.3.0
goos: windows
goarch: amd64
pkg: github.com/H0llyW00dzZ/fiber2fa
cpu: AMD Ryzen 9 3900X 12-Core Processor            
BenchmarkJSONSonicMiddlewareWithInvalidCookie-24         	  113605	      9290 ns/op	    6065 B/op	      29 allocs/op
BenchmarkJSONSonicWithValid2FA-24                        	   55086	     21073 ns/op	    9598 B/op	      66 allocs/op
BenchmarkJSONSonicWithValidCookie-24                     	   96120	     12311 ns/op	    7399 B/op	      41 allocs/op
BenchmarkJSONStdLibraryMiddlewareWithInvalidCookie-24    	  128434	      9386 ns/op	    6003 B/op	      29 allocs/op
BenchmarkJSONStdLibraryMiddlewareWithValid2FA-24         	   49399	     24714 ns/op	    8200 B/op	      68 allocs/op
BenchmarkJSONStdLibraryWithValidCookie-24                	   60553	     20039 ns/op	    7108 B/op	      46 allocs/op

[!NOTE] The benchmark results are based on the latest version of the middleware (v0.3.0) and were performed on a Windows machine with an AMD Ryzen 9 3900X 12-Core Processor. The results may vary depending on the system configuration and environment.

The benchmark tests cover different scenarios, including:

  • Middleware performance with an invalid cookie using the Sonic JSON library
  • Middleware performance with a valid 2FA token using the Sonic JSON library
  • Middleware performance with a valid cookie using the Sonic JSON library
  • Middleware performance with an invalid cookie using the standard library JSON package
  • Middleware performance with a valid 2FA token using the standard library JSON package
  • Middleware performance with a valid cookie using the standard library JSON package

The benchmark results provide insights into the performance characteristics of the middleware under different conditions and JSON libraries. It's important to consider these results when evaluating the middleware's suitability for specific use cases and performance requirements.

Also note that benchmark results may be updated in the future as the middleware evolves and new versions are released.

Contributing

Contributions are welcome! If you encounter any issues or have suggestions for improvements, please open an issue or submit a pull request.

License

This project is licensed under the BSD License.

Documentation

Overview

Package twofa provides a middleware for implementing two-factor authentication (2FA) in a Fiber application. It supports time-based one-time password (TOTP) authentication using the HMAC-based One-Time Password (HOTP) algorithm.

Installation

To use this middleware in a Fiber project, Go must be installed and set up.

1. Install the package using Go modules:

go get github.com/H0llyW00dzZ/fiber2fa

2. Import the package in the Fiber application:

import "github.com/H0llyW00dzZ/fiber2fa"

Usage

To use the 2FA middleware in a Fiber application, create a new instance of the middleware with the desired configuration and register it with the application.

Note: This 2FA middleware requires c.Locals to be set before using it. Use the fiber.Ctx.Locals middleware to set c.Locals.

package main

import (
	"github.com/gofiber/fiber/v2"
	"github.com/H0llyW00dzZ/fiber2fa"
)

func main() {
	app := fiber.New()

	// Use the fiber.Locals middleware to set c.Locals
	app.Use(func(c *fiber.Ctx) error {

		// Note: c.Locals is pretty useful and can directly store values from a database.
		c.Locals("email", "user@example.com")
		return c.Next()
	})

	app.Use(twofa.New(twofa.Config{
		Issuer:      "MyApp",
		ContextKey:  "email",
		Storage:     storage,
	}))

	// Register routes and start the server
	// ...

	app.Listen(":3000")
}

In the example above, the fiber.Locals middleware is used to set c.Locals with the "email" key and the corresponding value. This value can be accessed in the 2FA middleware using the ContextKey specified in the configuration.

The 2FA middleware is then created with a configuration that specifies the issuer name, context key, and storage provider.

Configuration

The 2FA middleware accepts a twofa.Config struct for configuration. The available options are:

  • Secret: The shared secret used for generating and verifying TOTP tokens. If not provided, a random secret will be generated using https://pkg.go.dev/github.com/xlzd/gotp#RandomSecret. Default is a random secret.
  • Issuer: The name of the issuer to be displayed in the authenticator app. Default is "MyApp".
  • AccountName: The name of the account to be displayed in the authenticator app. Deprecated: Use "ContextKey" Instead.
  • DigitsCount: The number of digits in the generated TOTP token. Default is 6.
  • Period: The time step size in seconds for generating TOTP tokens. Default is 30 seconds.
  • Hash: The hashing algorithm used for generating TOTP tokens. Default is otp.SHA512.
  • TimeSource: The time source used for generating TOTP tokens. Default is time.Now.
  • SyncWindow: The number of time steps to check before and after the current time step when verifying TOTP tokens. Default is otp.DefaultConfig.SyncWindow.
  • SkipCookies: A list of paths that should skip the 2FA middleware. Default is nil.
  • CookieName: The name of the cookie used to store the 2FA validation status. Default is "twofa_cookie".
  • CookieMaxAge: The maximum age of the 2FA cookie in seconds. Default is 86400 (24 hours).
  • CookiePath: The path scope of the 2FA cookie. Default is "/".
  • CookieDomain: The domain scope of the 2FA cookie. If set to "auto", it will automatically set the cookie domain based on the request's domain if HTTPS is used. Default is an empty string.
  • CookieSecure: Determines whether the 2FA cookie should be sent only over HTTPS. Default is false.
  • RedirectURL: The URL to redirect the user to when 2FA is required. Default is "/2fa".
  • Storage: The storage provider for storing 2FA information. Default is nil (in-memory storage).
  • StorageExpiration: The duration for which the 2FA information should be stored in the storage. Default is 0 (no expiration).
  • TokenLookup: A string in the form of "<source>:<name>" that is used to extract the token from the request. Default is "query:token".
  • ContextKey: The key used to store the 2FA information in the context. This field is required.
  • JSONMarshal: A custom JSON marshaling function. Default is json.Marshal.
  • JSONUnmarshal: A custom JSON unmarshaling function. Default is json.Unmarshal.
  • Next: An optional function that determines whether to skip the 2FA middleware for a given request. If the function returns true, the middleware will be skipped. Default is nil.
  • QRCode: The configuration for the QR code generation. It allows customizing the QR code path template, image, and content. Default is twofa.DefaultQRCodeConfig.
  • Encode: The configuration for the QR code encoding. It allows customizing the QR code recovery level and size. Default is twofa.DefaultEncodeConfig.
  • ResponseMIME: The MIME type for the response format. Default is fiber.MIMETextPlainCharsetUTF8. Possible values are:
  • fiber.MIMETextPlainCharsetUTF8 (default)
  • fiber.MIMEApplicationJSON
  • fiber.MIMEApplicationJSONCharsetUTF8
  • fiber.MIMEApplicationXML
  • fiber.MIMEApplicationXMLCharsetUTF8
  • fiber.MIMETextPlain
  • fiber.MIMETextHTML (custom handler required)
  • fiber.MIMETextHTMLCharsetUTF8 (custom handler required)
  • fiber.MIMETextJavaScript (custom handler required)
  • fiber.MIMETextJavaScriptCharsetUTF8 (custom handler required)
  • fiber.MIMEApplicationForm (custom handler required)
  • fiber.MIMEMultipartForm (custom handler required)
  • fiber.MIMEOctetStream (custom handler required)
  • UnauthorizedHandler: A custom handler for unauthorized responses. Default is nil.
  • InternalErrorHandler: A custom handler for internal server error responses. Default is nil.
  • IdentifierGenerator: A function that generates a unique identifier for the 2FA registration. Default is nil (uses fiber utils.UUIDv4 generator).

Storage Providers

The 2FA middleware requires a storage provider to store the 2FA information for each user. The storage provider should implement the fiber.Storage interface.

You can use any storage provider that implements the fiber.Storage interface, such as:

The 2FA information is stored in the storage using the ContextKey as the unique identifier. The ContextKey is bound to the raw value (2FA information) in the storage.

QR Code Generation

The 2FA middleware provides a route for generating QR codes that can be scanned by authenticator apps to set up 2FA for a user.

By default, the QR code generation route is accessible at "/2fa/register?account=<account_name>". You can customize the path template by modifying the PathTemplate field in the twofa.QRCodeConfig struct.

The QR code image can be customized by providing a custom image in the Image field of the twofa.QRCodeConfig struct. If a custom image is provided, it will be used as the background image for the QR code.

The content of the QR code can be customized by modifying the Content field in the twofa.QRCodeConfig struct. The default content format is "otpauth://totp/%s:%s?secret=%s&issuer=%s".

Custom QR Code Image Generation (Advanced use cases)

The 2FA middleware allows generating custom QR code images for use with custom mobile apps or physical devices. This feature provides flexibility in integrating 2FA with custom cryptography and scanning mechanisms.

To generate a custom QR code image, provide a custom image in the Image field of the twofa.QRCodeConfig struct. The custom image should be of type image.Image.

When a custom image is provided, the middleware will generate a QR code and overlay it on top of the custom image. The resulting QR code image can be scanned by a custom mobile app or physical device that supports QR code scanning.

By using a custom QR code image, it's possible to incorporate custom branding, design, or additional information into the QR code. This allows creating a seamless and integrated 2FA experience for users.

Additionally, custom cryptography techniques can be leveraged to secure the QR code data. Instead of using the default TOTP algorithm, custom encryption and decryption mechanisms can be implemented to protect the shared secret and other sensitive information embedded in the QR code.

Furthermore, the custom QR code image generation feature enables extending 2FA beyond mobile apps. The QR code can be bound to physical devices or objects that have scanning capabilities, such as smart cards, badges, or dedicated hardware tokens. This provides an additional layer of security and convenience for users who prefer physical authentication methods.

To implement custom QR code image generation, follow these steps:

  1. Create a custom image of type image.Image that will serve as the background for the QR code.
  2. Set the custom image in the Image field of the twofa.QRCodeConfig struct when configuring the 2FA middleware.
  3. Implement a custom mobile app or physical device that can scan the custom QR code image and extract the necessary information for 2FA.
  4. Optionally, implement custom cryptography techniques to secure the QR code data and ensure the integrity and confidentiality of the shared secret.

By leveraging custom QR code image generation, it's possible to create a unique and secure 2FA experience tailored to specific requirements and user preferences.

Error Handling

The 2FA middleware handles errors internally and returns appropriate HTTP status codes and error messages.

If an error occurs during the 2FA process, the middleware will return a response with a status code of 401 (Unauthorized) or 500 (Internal Server Error), depending on the nature of the error.

The error messages are sent in the specified response format (MIME type) configured in the ResponseMIME field of the twofa.Config struct. The default response format is plain text (fiber.MIMETextPlainCharsetUTF8).

You can customize the error handling by providing custom handlers for unauthorized and internal server errors using the UnauthorizedHandler and InternalErrorHandler fields in the twofa.Config struct.

Error Variables

The 2FA middleware defines several error variables that represent different types of errors that can occur during the 2FA process. These error variables are:

These error variables are used by the middleware to provide meaningful error messages when errors occur during the 2FA process.

Skipping 2FA

You can skip the 2FA middleware for certain routes by specifying the paths in the SkipCookies field of the twofa.Config struct.

Additionally, you can provide a custom function in the Next field of the twofa.Config struct to determine whether to skip the 2FA middleware for a given request. If the function returns true, the middleware will be skipped.

Info Management

The 2FA middleware uses the twofa.Info struct to manage the 2FA information for each user. The twofa.Info struct implements the twofa.InfoManager interface, which defines methods for accessing and modifying the 2FA information.

The twofa.Info struct contains the following fields:

The twofa.InfoManager interface provides methods for accessing and modifying these fields.

The 2FA middleware uses cookies to store the 2FA validation status for each user. The cookie-related configurations can be customized using the following fields in the twofa.Config struct:

  • CookieName: The name of the cookie used to store the 2FA validation status.
  • CookieMaxAge: The maximum age of the 2FA cookie in seconds.
  • CookiePath: The path scope of the 2FA cookie.
  • CookieDomain: The domain scope of the 2FA cookie. If set to "auto", it will automatically set the cookie domain based on the request's domain if HTTPS is used.
  • CookieSecure: Determines whether the 2FA cookie should be sent only over HTTPS.

The middleware generates a signed cookie value using HMAC to ensure the integrity of the cookie. The cookie value contains the expiration time of the cookie.

Token Verification

The 2FA middleware verifies the TOTP token provided by the user during the 2FA process. The token can be extracted from various sources such as query parameters, form data, cookies, headers, or URL parameters.

The token lookup configuration is specified using the TokenLookup field in the twofa.Config struct. It follows the format "<source>:<name>", where <source> can be "query", "form", "cookie", "header", or "param", and <name> is the name of the parameter or key.

If a valid token is provided, the middleware sets a 2FA cookie to indicate that the user has successfully completed the 2FA process. The cookie value is generated using the twofa.Middleware.GenerateCookieValue function, which signs the cookie value using HMAC.

Identifier Generation

The 2FA middleware generates a unique identifier for each 2FA registration. The identifier is used to associate the 2FA information with a specific user or account.

By default, the middleware uses the github.com/gofiber/utils.UUIDv4 function to generate a random UUID as the identifier.

The identifier generation can be customized by providing a custom function in the IdentifierGenerator field of the twofa.Config struct. The custom function should take a *fiber.Ctx as a parameter and return a string identifier.

The generated identifier is stored in the twofa.Info struct and can be accessed using the twofa.Info.GetIdentifier method.

 	// Example of a custom identifier generator function.
	func customIdentifierGenerator(c *fiber.Ctx) string {
		// Generate a custom identifier based on the request context
		identifier := // Custom logic to generate the identifier
		return identifier
	}

	app.Use(twofa.New(twofa.Config{
		Issuer:              "MyApp",
		ContextKey:          "email",
		Storage:             storage,
		IdentifierGenerator: customIdentifierGenerator,
	}))

In the example above, the customIdentifierGenerator function is provided as the value for the IdentifierGenerator field in the twofa.Config struct. This function will be called by the middleware to generate the identifier for each 2FA registration.

The custom identifier generator function can access the request context through the *fiber.Ctx parameter and generate the identifier based on any relevant information available in the context, such as user ID, email, or any other unique attribute.

Providing a custom identifier generator allows for the flexibility to generate identifiers that are specific to the application's requirements and ensures uniqueness and compatibility with the existing user or account management system.

Note: If the IdentifierGenerator field is not provided or set to nil, the middleware will use the default identifier generator, which generates a random UUID using github.com/gofiber/utils.UUIDv4.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrorFailedToRetrieveInfo       = errors.New("failed to retrieve 2FA information")
	ErrorFailedToUnmarshalInfo      = errors.New("failed to unmarshal 2FA information")
	ErrorFailedToMarshalInfo        = errors.New("failed to marshal updated 2FA information")
	ErrorFailedToStoreInfo          = errors.New("failed to store updated 2FA information")
	ErrorFailedToDeleteInfo         = errors.New("failed to delete 2FA information")
	ErrorFailedToResetStorage       = errors.New("failed to reset storage")
	ErrorFailedToCloseStorage       = errors.New("failed to close storage")
	ErrorContextKeyNotSet           = errors.New("ContextKey is not set")
	ErrorFailedToRetrieveContextKey = errors.New("failed to retrieve context key")
)

Global error variables

View Source
var DefaultConfig = Config{
	Secret:               gotp.RandomSecret(16),
	Issuer:               "MyApp",
	DigitsCount:          otp.DefaultConfig.Digits,
	Period:               otp.DefaultConfig.Period,
	Hash:                 otp.SHA512,
	TimeSource:           otp.DefaultConfig.TOTPTime,
	SyncWindow:           otp.DefaultConfig.SyncWindow,
	SkipCookies:          nil,
	CookieName:           "twofa_cookie",
	CookieMaxAge:         86400,
	CookiePath:           "/",
	CookieDomain:         "",
	CookieSecure:         false,
	RedirectURL:          "/2fa",
	Storage:              nil,
	StorageExpiration:    0,
	TokenLookup:          "query:token",
	ContextKey:           "",
	JSONMarshal:          json.Marshal,
	JSONUnmarshal:        json.Unmarshal,
	Next:                 nil,
	QRCode:               DefaultQRCodeConfig,
	Encode:               DefaultEncodeConfig,
	ResponseMIME:         fiber.MIMETextPlainCharsetUTF8,
	UnauthorizedHandler:  nil,
	InternalErrorHandler: nil,
	IdentifierGenerator:  nil,
}

DefaultConfig holds the default configuration values.

View Source
var DefaultEncodeConfig = EncodeConfig{
	Level:         qrcode.Medium,
	Size:          256,
	VersionNumber: 0,
}

DefaultEncodeConfig holds the default configuration values for the QR code encoding.

View Source
var DefaultQRCodeConfig = QRCodeConfig{
	Image:   nil,
	Content: "otpauth://totp/%s:%s?secret=%s&issuer=%s",

	PathTemplate: "/2fa/register?account=%s",
}

DefaultQRCodeConfig holds the default configuration values for the QR code generation.

Functions

func New

func New(config ...Config) fiber.Handler

New creates a new instance of the 2FA middleware with the provided configuration.

Types

type Config

type Config struct {
	// Secret is the shared secret used for generating and verifying TOTP tokens.
	//
	// Optional. Default: "gotp.RandomSecret(16)" (Recommended).
	Secret string

	// Issuer is the name of the issuer to be displayed in the authenticator app.
	//
	// Optional. Default: "MyApp"
	Issuer string

	// AccountName is the name of the account to be displayed in the authenticator app.
	//
	// Deprecated: Use "ContextKey" Instead.
	AccountName string

	// DigitsCount is the number of digits in the generated TOTP token.
	//
	// Optional. Default: 6
	DigitsCount int

	// Period is the time step size in seconds for generating TOTP tokens.
	//
	// Optional. Default: 30
	Period int

	// Hash is the hashing algorithm used for generating TOTP tokens.
	//
	// Optional. Default: otp.SHA512
	Hash string

	// TimeSource is the time source used for generating TOTP tokens.
	//
	// Optional. Default: otp.DefaultConfig.TOTPTime()
	TimeSource otp.TimeSource

	// SyncWindow is the number of time steps to check before and after the current time step when verifying TOTP tokens.
	//
	// Optional. Default: otp.DefaultConfig.SyncWindow
	SyncWindow int

	// SkipCookies is a list of paths that should skip the 2FA middleware.
	//
	// Optional. Default: nil
	SkipCookies []string

	// CookieName is the name of the cookie used to store the 2FA validation status.
	//
	// Optional. Default: "twofa_cookie"
	CookieName string

	// CookieMaxAge is the maximum age of the 2FA cookie in seconds.
	//
	// Optional. Default: 86400 (24 hours)
	CookieMaxAge int

	// CookiePath is the path scope of the 2FA cookie.
	//
	// Optional. Default: "/"
	CookiePath string

	// CookieDomain is the domain scope of the 2FA cookie.
	//
	// If set to "auto", it will automatically set the cookie domain based on the request's domain if HTTPS is used.
	//
	// Optional. Default: ""
	CookieDomain string

	// CookieSecure determines whether the 2FA cookie should be sent only over HTTPS.
	//
	// Optional. Default: false
	CookieSecure bool

	// RedirectURL is the URL to redirect the user to when 2FA is required.
	//
	// Optional. Default: "/2fa"
	RedirectURL string

	// Storage is the storage provider for storing 2FA information.
	//
	// Optional. Default: nil (in-memory storage)
	Storage fiber.Storage

	// StorageExpiration is the duration for which the 2FA information should be stored in the storage.
	//
	// Optional. Default: 0 (no expiration)
	StorageExpiration time.Duration

	// TokenLookup is a string in the form of "<source>:<name>" that is used to extract the token from the request.
	//
	// Optional. Default value "query:token".
	//
	// Possible values:
	//
	//  - "header:<name>"
	//  - "query:<name>"
	//  - "form:<name>"
	//  - "param:<name>"
	//  - "cookie:<name>"
	TokenLookup string

	// ContextKey is the key used to store the 2FA information in the context.
	//
	// Required.
	ContextKey string

	// JSONMarshal is a custom JSON marshaling function.
	//
	// Optional. Default: json.Marshal
	JSONMarshal JSONMarshal

	// JSONUnmarshal is a custom JSON unmarshaling function.
	//
	// Optional. Default: json.Unmarshal
	JSONUnmarshal JSONUnmarshal

	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// QRcodeImage is the custom barcode image to be used instead of the default QR code.
	//
	// Deprecated: replaced by "QRCode"
	QRcodeImage image.Image

	// QRCode is the configuration for the QR code generation.
	// It allows customizing the QR code path template, image, and content.
	//
	// Optional. Default: see DefaultQRCodeConfig
	QRCode QRCodeConfig

	// Encode is the configuration for the QR code encoding.
	//
	// Optional. Default: see DefaultEncodeConfig
	Encode EncodeConfig

	// ResponseMIME is the MIME type for the response format.
	//
	// This field is used to set the default response format for the middleware.
	// When custom error handlers (UnauthorizedHandler and InternalErrorHandler) are not provided,
	// the middleware will use the ResponseMIME value to determine the response format.
	//
	// Optional. Default: fiber.MIMETextPlainCharsetUTF8
	//
	// Possible values:
	//  - fiber.MIMETextPlainCharsetUTF8 (default)
	//  - fiber.MIMEApplicationJSON
	//  - fiber.MIMEApplicationJSONCharsetUTF8
	//  - fiber.MIMEApplicationXML
	//  - fiber.MIMEApplicationXMLCharsetUTF8
	//  - fiber.MIMETextPlain
	//  - fiber.MIMETextHTML (custom handler required)
	//  - fiber.MIMETextHTMLCharsetUTF8 (custom handler required)
	//  - fiber.MIMETextJavaScript (custom handler required)
	//  - fiber.MIMETextJavaScriptCharsetUTF8 (custom handler required)
	//  - fiber.MIMEApplicationForm (custom handler required)
	//  - fiber.MIMEMultipartForm (custom handler required)
	//  - fiber.MIMEOctetStream (custom handler required)
	//
	// When using custom error handlers, you can set the response format within the handler functions
	// using the appropriate methods provided by the Fiber context (c.JSON(), c.XML(), c.SendString(), etc.).
	// The custom error handlers allow you to use any valid MIME type supported by Fiber for the response format.
	//
	// Examples of possible response formats for custom error handlers:
	//  - Plain text: c.SendString() with content type fiber.MIMETextPlain or fiber.MIMETextPlainCharsetUTF8
	//  - HTML: c.SendString() with content type fiber.MIMETextHTML or fiber.MIMETextHTMLCharsetUTF8
	//  - XML: c.XML() with content type fiber.MIMEApplicationXML or fiber.MIMEApplicationXMLCharsetUTF8
	//  - JSON: c.JSON() with content type fiber.MIMEApplicationJSON or fiber.MIMEApplicationJSONCharsetUTF8
	//  - JavaScript: c.SendString() with content type fiber.MIMETextJavaScript or fiber.MIMETextJavaScriptCharsetUTF8
	//  - Form data: c.SendString() with content type fiber.MIMEApplicationForm
	//  - Custom MIME type: c.Send() with the desired MIME type
	//
	// Note: When custom error handlers are provided, the ResponseMIME value is not used for the response format.
	// The response format is determined by the methods used within the custom error handlers.
	ResponseMIME string

	// UnauthorizedHandler is a custom handler for unauthorized responses.
	//
	// Optional. Default: nil
	UnauthorizedHandler fiber.ErrorHandler

	// InternalErrorHandler is a custom handler for internal server error responses.
	//
	// Optional. Default: nil
	InternalErrorHandler fiber.ErrorHandler

	// IdentifierGenerator is a function that generates a unique identifier for the 2FA registration.
	//
	// The function takes a *fiber.Ctx as a parameter and returns a string identifier.
	// It allows customizing the identifier generation based on the request context.
	//
	// If not provided, the default identifier generator will be used, which generates a UUID.
	//
	// Optional. Default: nil (uses fiber utils.UUIDv4 generator)
	IdentifierGenerator func(*fiber.Ctx) string
}

Config defines the configuration options for the 2FA middleware.

type EncodeConfig

type EncodeConfig struct {
	// Level is the QR code recovery level.
	//
	// Optional. Default: qrcode.Medium
	Level qrcode.RecoveryLevel

	// Size is the size of the QR code image.
	//
	// Optional. Default: 256
	Size int

	// VersionNumber is the QR code version number.
	//
	// Optional. Default: 0 (automatically determined)
	VersionNumber int
}

EncodeConfig defines the configuration options for the QR code encoding.

type Info

type Info struct {
	ContextKey     string    `json:"contextkey,omitempty"`
	Secret         string    `json:"secret"`
	CookieValue    string    `json:"cookie,omitempty"`
	ExpirationTime time.Time `json:"expiration,omitempty"`
	Registered     bool      `json:"registered"`
	Identifier     string    `json:"identifier,omitempty"`
	QRCodeData     []byte    `json:"qrcodedata,omitempty"`
}

Info represents the 2FA information stored for a user.

func NewInfo added in v0.3.0

func NewInfo(cfg *Config) *Info

NewInfo creates a new empty Info struct based on the provided Config.

func (*Info) GetCookieValue

func (i *Info) GetCookieValue() string

GetCookieValue returns the cookie value.

func (*Info) GetExpirationTime

func (i *Info) GetExpirationTime() time.Time

GetExpirationTime returns the cookie expiration time.

func (*Info) GetIdentifier added in v0.3.2

func (i *Info) GetIdentifier() string

GetIdentifier returns the identifier.

func (*Info) GetQRCodeData added in v0.3.3

func (i *Info) GetQRCodeData() []byte

GetQRCodeData returns the QRCode data.

func (*Info) GetSecret

func (i *Info) GetSecret() string

GetSecret returns the secret for 2FA.

func (*Info) IsRegistered added in v0.3.2

func (i *Info) IsRegistered() bool

IsRegistered returns the registration status.

func (*Info) SetContextKey added in v0.3.0

func (i *Info) SetContextKey(contextKey string)

SetContextKey sets the context key in the Info struct.

func (*Info) SetCookieValue

func (i *Info) SetCookieValue(value string)

SetCookieValue sets the cookie value.

func (*Info) SetExpirationTime

func (i *Info) SetExpirationTime(expiration time.Time)

SetExpirationTime sets the cookie expiration time.

func (*Info) SetIdentifier added in v0.3.2

func (i *Info) SetIdentifier(identifier string)

SetIdentifier sets the identifier.

func (*Info) SetQRCodeData added in v0.3.3

func (i *Info) SetQRCodeData(data []byte)

SetQRCodeData sets the QRCode data.

func (*Info) SetRegistered added in v0.3.2

func (i *Info) SetRegistered(registered bool)

SetRegistered sets the registration status.

func (*Info) SetSecret added in v0.3.0

func (i *Info) SetSecret(secret string)

SetSecret sets the secret for 2FA.

type InfoManager

type InfoManager interface {
	GetSecret() string
	GetCookieValue() string
	GetExpirationTime() time.Time
	IsRegistered() bool
	GetIdentifier() string
	GetQRCodeData() []byte
	SetSecret(secret string)
	SetCookieValue(value string)
	SetExpirationTime(expiration time.Time)
	SetContextKey(contextKey string)
	SetRegistered(registered bool)
	SetIdentifier(identifier string)
	SetQRCodeData(data []byte)
}

InfoManager defines the interface for managing 2FA information.

type JSONMarshal

type JSONMarshal func(v any) ([]byte, error)

JSONMarshal defines the function signature for a JSON marshal.

type JSONUnmarshal

type JSONUnmarshal func(data []byte, v any) error

JSONUnmarshal defines the function signature for a JSON unmarshal.

type Middleware

type Middleware struct {
	Config       *Config
	Info         InfoManager
	PathHandlers []PathHandler
}

Middleware represents the 2FA middleware.

func (*Middleware) GenerateCookieValue

func (m *Middleware) GenerateCookieValue(expirationTime time.Time) string

GenerateCookieValue generates a signed cookie value using HMAC.

TODO: Implement an extra layer of cookie value (in addition to the current timestamp) and enhance security by using custom cryptography for encryption and decryption value. Use a user secret derived from 2FA for encryption/decryption and bind it to a UUID for identification purposes. This will replace the current implementation that uses HMAC.

func (*Middleware) GenerateIdentifier added in v0.4.2

func (m *Middleware) GenerateIdentifier(c *fiber.Ctx) string

GenerateIdentifier generates an identifier using the configured identifier generator.

func (*Middleware) GenerateQRcodePath

func (m *Middleware) GenerateQRcodePath(c *fiber.Ctx) error

GenerateQRcodePath generates the QR code image for the 2FA secret key and stores the QR code data.

TODO: Improve this function by using an otpverifier (internal) package. The QRCodePath (this function) should be the location where the user wants to scan the QR code. For example, if the user registers from example.com/2fa/register, then this is the place for the QR code image: example.com/2fa/register/scanqrcode/b689a842-065f-4664-xxxx-xxxxxxxxx.png (note: "b689a842-065f-4664-xxxx-xxxxxxxxx" is a UUID). After the user completes scanning the QR code at example.com/2fa/register, this path will redirect to another page using c.Next(). Also Note that It's possible to improve this method using the above approach without relying on or needing a filesystem such as a file manager (e.g., for storing the QR code image), since this is written in Go.

func (*Middleware) Handle

func (m *Middleware) Handle(c *fiber.Ctx) error

Handle is the method on Middleware that handles the 2FA authentication process.

func (*Middleware) SendInternalErrorResponse added in v0.2.0

func (m *Middleware) SendInternalErrorResponse(c *fiber.Ctx, err error) error

SendInternalErrorResponse sends an internal server error response based on the configured MIME type.

func (*Middleware) SendUnauthorizedResponse added in v0.2.0

func (m *Middleware) SendUnauthorizedResponse(c *fiber.Ctx, err error) error

SendUnauthorizedResponse sends an unauthorized response based on the configured MIME type.

type PathHandler added in v0.4.5

type PathHandler struct {
	// Path is the URL path to match for the handler.
	Path string

	// Handler is the fiber.Handler function to be executed when the path matches.
	Handler fiber.Handler
}

PathHandler represents a path handler for the 2FA middleware.

type QRCodeConfig

type QRCodeConfig struct {
	// PathTemplate is the template for the QR code path.
	//
	// Optional. Default: "/2fa/register?account=%s"
	PathTemplate string

	// Image is the custom QR code image to be used instead of the default QR code.
	//
	// Optional. Default: nil
	Image image.Image

	// Content is the template for the QR code content.
	//
	// Optional. Default: "otpauth://totp/%s:%s?secret=%s&issuer=%s"
	Content string
}

QRCodeConfig defines the configuration options for the QR code generation.

Directories

Path Synopsis
internal
crypto/hash/blake2botp
Package blake2botp provides BLAKE2b-based hash implementations for use with one-time passwords (OTP).
Package blake2botp provides BLAKE2b-based hash implementations for use with one-time passwords (OTP).
crypto/hash/blake3otp
Package blake3otp provides BLAKE3-based hash implementations for use with one-time passwords (OTP).
Package blake3otp provides BLAKE3-based hash implementations for use with one-time passwords (OTP).
otpverifier
Package otpverifier provides a simple and flexible way to verify and generate one-time passwords (OTPs) using time-based (TOTP) or counter-based (HOTP) algorithms.
Package otpverifier provides a simple and flexible way to verify and generate one-time passwords (OTPs) using time-based (TOTP) or counter-based (HOTP) algorithms.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL