authcred

package
v0.0.0-...-9a1549d Latest Latest
Warning

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

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

Documentation

Overview

Package authcred mints and verifies the credentials both apps' auth flows are built on: session tokens, invite codes, telegram auth/login codes, and password hashes.

Index

Constants

View Source
const (
	SessionTokenBytes    = 32
	InviteCodeBytes      = 12
	TelegramAuthBytes    = 12
	TelegramLoginCodeLen = 8

	PasswordMinLen = 8
	PasswordMaxLen = 72 // bcrypt truncates beyond this

	// TelegramLoginCodeAlphabet is the human-typed alphabet: uppercase letters
	// and digits, so a code can be read aloud and retyped without ambiguity.
	TelegramLoginCodeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
View Source
const SessionRefreshInterval = time.Minute

SessionRefreshInterval is the minimum gap between sessions.last_seen_at writes for a given session. Most authenticated requests are served from a single SELECT; only every ~minute do we round-trip a write to extend the sliding lifetime.

Variables

This section is empty.

Functions

func CreateSession

func CreateSession(ctx context.Context, ex Execer, userID int64, now time.Time) (string, error)

CreateSession inserts a session row and returns the raw token — only the hash is stored. A token collision is astronomically unlikely but cheap to retry; any other error is real and returned at once.

func DeleteSession

func DeleteSession(ctx context.Context, ex Execer, sessionID int64) error

DeleteSession forgets one session (an expired one, a logout).

func HashPassword

func HashPassword(password string) (string, error)

HashPassword returns a bcrypt hash. The bcrypt format embeds its own salt, so callers pass none in.

func HashSessionToken

func HashSessionToken(token string) string

HashSessionToken hashes a raw session token into the value stored in sessions.token_hash.

func LegacySHA256Password

func LegacySHA256Password(password, salt string) string

LegacySHA256Password is the pre-bcrypt scheme: sha256(salt + ":" + password).

func NeedsRefresh

func NeedsRefresh(lastSeen, expiry, now time.Time) bool

NeedsRefresh reports whether a session's sliding expiry should be extended. It says yes once last_seen_at has drifted by SessionRefreshInterval — and also whenever expires_at has drifted in from the full lifetime, which keeps the sliding window correct if something (an admin tool, a migration, a test) shortened expires_at independently of last_seen_at.

func NewInviteCode

func NewInviteCode() (string, error)

func NewSessionToken

func NewSessionToken() (string, error)

func NewTelegramAuthCode

func NewTelegramAuthCode() (string, error)

func NewTelegramLoginCode

func NewTelegramLoginCode() (string, error)

func RandomBase32

func RandomBase32(n int) (string, error)

RandomBase32 returns n random bytes as unpadded uppercase base32.

func SlideSession

func SlideSession(ctx context.Context, ex Execer, sessionID int64, now time.Time) error

SlideSession extends a live session by the full Lifetime from now.

func VerifyPassword

func VerifyPassword(storedHash, password string) bool

VerifyPassword checks a candidate against a bcrypt hash.

func VerifyPasswordUpgrading

func VerifyPasswordUpgrading(storedHash, storedSalt, password string) (ok bool, upgraded string, err error)

VerifyPasswordUpgrading checks a candidate against a stored hash that may be either bcrypt or the legacy SHA256 scheme. On a legacy match it returns a fresh bcrypt hash in upgraded, which the caller should persist so the next login no longer relies on the weaker scheme. Apps with no legacy rows should call VerifyPassword instead.

Types

type Execer

type Execer interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}

Execer is the write side of a session store: a *sql.Tx, or any type that routes the write through the app's own write discipline.

type Queryer

type Queryer interface {
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

Queryer is the read side of a session store.

type SessionState

type SessionState int

SessionState is what a token resolves to.

const (
	NoSession SessionState = iota // unknown token
	Expired                       // a row past its expiry — the caller deletes it
	Live
)

type Sessions

type Sessions struct {
	UserColumns string
	UserDest    func(u *session.User) (dest []any, done func())
}

Sessions resolves cookies over an app's users table. The session columns are the same in both apps; the user columns differ (dope reads is_system), so the app names the ones the join selects — prefixed "u." — and says where they scan: UserDest answers the destinations in column order and a step to run once scanned (an int column into a bool), or nil.

func (Sessions) Lookup

func (s Sessions) Lookup(ctx context.Context, q Queryer, token string, now time.Time) (u session.User, state SessionState, slide bool)

Lookup resolves a raw session token in one statement: the user, the state, and whether the sliding expiry is due (NeedsRefresh) — the caller slides it with SlideSession under its own write discipline, as it deletes an Expired row. A scan error reads as NoSession, like a missing row.

Jump to

Keyboard shortcuts

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