twofa

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package twofa generates and verifies TOTP secrets for the CSuit apps.

Index

Constants

View Source
const CallbackPrefix = "tg2fa:"

CallbackPrefix starts the callback data of the approval buttons; a bot hands any callback that starts with it to HandleCallback. The data is tg2fa:<tenant>:<token>:<y|n>, so a shared bot's hub can find the tenant that asked, the same way it does for sign-in buttons.

Variables

View Source
var ErrEmailCooldown = errors.New("twofa: an email code was sent a moment ago")
View Source
var (
	ErrNoRedis = errors.New("twofa: redis unavailable")
)

Functions

func CallbackTenant added in v1.8.0

func CallbackTenant(data string) (string, bool)

CallbackTenant returns the tenant named in an approval button's data, for a hub that has to pass the press on to the tenant that asked.

func CheckEmailCode added in v1.8.0

func CheckEmailCode(tenant string, userID int64, code string) (bool, error)

CheckEmailCode reports whether the code matches the one last sent, and uses it up when it does. After five wrong tries the code is gone.

func ClearDone added in v1.8.0

func ClearDone(sessionKey string)

func DiscardEmailCode added in v1.8.0

func DiscardEmailCode(tenant string, userID int64)

DiscardEmailCode forgets a code that could not be delivered.

func Done added in v1.8.0

func Done(sessionKey string) (map[Method]bool, error)

func HandleCallback added in v1.8.0

func HandleCallback(data string, chatID int64) (handled, approved, accepted bool)

HandleCallback is what a bot calls with the data of a pressed button. handled is false for callbacks that are not approval buttons. approved is the person's answer; accepted is false when the approval was already answered, expired or pressed from another chat.

func MarkDone added in v1.8.0

func MarkDone(sessionKey string, m Method) error

MarkDone remembers that a method was completed for one sign-in in progress (sessionKey is the app's own pending-2FA token). It is how "require all methods" carries progress from one step to the next.

func MarkReauth added in v1.8.0

func MarkReauth(tenant string, userID int64, m Method) error

MarkReauth records that a signed-in user just proved a method for a sensitive action (an approval pressed in Telegram, or an emailed code). It lasts two minutes and is used up by the first TakeReauth of the same method.

func NewEmailCode added in v1.8.0

func NewEmailCode(tenant string, userID int64) (string, error)

NewEmailCode makes a six digit code for the person and remembers only its hash for ten minutes. A new code replaces the old one; asking again within thirty seconds returns ErrEmailCooldown. The caller emails the code and, when that fails, calls DiscardEmailCode so the person can ask again at once.

func NewSecret

func NewSecret(issuer, account string) (string, error)

NewSecret returns a fresh base32 TOTP secret for the account.

func PollApproval added in v1.8.0

func PollApproval(token, secret string) (status string, userID int64)

PollApproval is called by the waiting page. An approved answer is returned once and then forgotten.

func QRDataURL

func QRDataURL(issuer, secret, account string) (string, error)

QRDataURL renders the setup QR code for an existing base32 secret as a data:image/png URL. The secret is base32-decoded first so the code encodes the same secret that Verify checks against.

func StartApproval added in v1.8.0

func StartApproval(ctx context.Context, bot *telegram.Bot, tenant string, userID, chatID int64, tx Texts) (token, secret string, err error)

StartApproval records a pending approval for the person and sends them the message with its two buttons. The returned secret is for the page that is waiting: only its hash is stored, so a leaked button cannot sign anyone in.

func StartApprovalWith added in v1.8.0

func StartApprovalWith(ctx context.Context, send func(ctx context.Context, chat, text string, buttons []telegram.Button) error, tenant string, userID, chatID int64, tx Texts) (token, secret string, err error)

StartApprovalWith is StartApproval for callers that deliver the buttons through something other than a bot they hold, such as a relay.

func TakeReauth added in v1.8.0

func TakeReauth(tenant string, userID int64, m Method) (bool, error)

TakeReauth reports whether a proof from MarkReauth is waiting, and uses it up.

func Verify

func Verify(secret, code string) bool

Verify checks a code against a plaintext base32 secret.

func VerifyStep added in v1.2.4

func VerifyStep(secret, code string, now time.Time) (int64, bool)

VerifyStep checks a code like Verify and also returns the 30-second step it matched. Store the step per user and accept only a larger one next time, so a code cannot be used twice inside its validity window.

Types

type Method added in v1.8.0

type Method string
const (
	MethodTOTP     Method = "totp"
	MethodTelegram Method = "telegram"
	MethodEmail    Method = "email"
)

type Prefs added in v1.8.0

type Prefs struct {
	Telegram      bool `json:"telegram,omitempty"`
	Email         bool `json:"email,omitempty"`
	All           bool `json:"all,omitempty"`
	PasskeySkips  bool `json:"passkey_skips,omitempty"`
	MagicSkips    bool `json:"magic_skips,omitempty"`
	TelegramSkips bool `json:"telegram_skips,omitempty"`
}

Prefs is what a person chose on their 2FA page besides which methods are switched on: whether every enabled method is required or any one will do, whether a passkey sign-in skips 2FA, and whether Telegram approval is on. It is stored as one small JSON string next to the account.

func ParsePrefs added in v1.8.0

func ParsePrefs(raw string) Prefs

func (Prefs) Enabled added in v1.8.0

func (p Prefs) Enabled(totpOn, telegramLinked bool) []Method

Enabled lists the methods that apply to this account: the authenticator when it is set up, Telegram when the person turned it on and has a chat linked, and email when the person turned it on (every account has an address).

func (Prefs) Encode added in v1.8.0

func (p Prefs) Encode() string

func (Prefs) NeedsStep added in v1.8.0

func (p Prefs) NeedsStep(v Via, enabled []Method) bool

NeedsStep reports whether a sign-in by this route still needs a 2FA step. A magic link proves the email method and a Telegram sign-in proves the Telegram method, so each counts as that method being done; the person's own "skip 2FA for this route" choice is honoured for passkey, magic link and Telegram, never for a password or a password reset.

func (Prefs) ProofSatisfied added in v1.8.0

func (p Prefs) ProofSatisfied(tenant string, userID int64, enabled []Method, totpOK bool, proven ...Method) (bool, error)

ProofSatisfied decides a sensitive-action check for a signed-in person. totpOK says whether a valid authenticator or recovery code came with the request, and proven lists methods already checked in this request. The other methods are proven earlier, on the page, and left a proof that is used up here. In "all" mode nothing is used up until the code is right.

func (Prefs) Remaining added in v1.8.0

func (p Prefs) Remaining(enabled []Method, done map[Method]bool) []Method

Remaining lists the enabled methods not yet completed.

func (Prefs) Satisfied added in v1.8.0

func (p Prefs) Satisfied(enabled []Method, done map[Method]bool) bool

Satisfied reports whether the methods completed so far are enough.

func (Prefs) SkipsFor added in v1.8.0

func (p Prefs) SkipsFor(viaPasskey bool, enabled []Method) bool

SkipsFor reports whether a sign-in needs no 2FA step at all.

type Texts added in v1.8.0

type Texts struct {
	Prompt  string
	Approve string
	Deny    string
}

Texts is the wording of the approval message, already in the person's language.

type Via added in v1.8.0

type Via string

Via is how a person proved who they are before the 2FA step.

const (
	ViaPassword Via = "password"
	ViaPasskey  Via = "passkey"
	ViaMagic    Via = "magic"
	ViaTelegram Via = "telegram"
)

Jump to

Keyboard shortcuts

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