auth

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package auth holds the authentication primitives that were previously only present as generated code.

It is deliberately narrow. #52 proposes extracting the whole authentication system as a framework-agnostic module, which is a larger and riskier piece of work than it looks -- roughly 1,100 lines currently living in templates, plus store interfaces that do not exist yet, plus a security review that the issue itself sets as a precondition for tagging. Cramming that into the tail of a release is how auth libraries ship with the bug they were written to avoid.

What is here instead is the part that is self-contained, has no dependency on a store or a session, and is where two of this project's four advisories actually lived: API token generation and verification. Generated code cannot be unit-tested, which is precisely why those defects reached users. This can.

The remaining flows -- users, sessions, 2FA, password reset -- stay in templates until they get their own change with its own review.

Example

Example shows the two halves of a token's life. It is compiled and run by `go test`, so it cannot drift from the API the way a README snippet can.

// At issue time: show the plaintext to the user, store only the hash.
issued, err := NewToken(24 * time.Hour)
if err != nil {
	panic(err)
}
stored := struct {
	Hash   []byte
	Expiry time.Time
}{issued.Hash, issued.Expiry}

// At request time: the plaintext arrives in a header, and the stored hash
// is all that is needed to check it.
presented, err := FromAuthorizationHeader("Bearer " + issued.PlainText)
if err != nil {
	panic(err)
}

check := &Token{Hash: stored.Hash, Expiry: stored.Expiry}
fmt.Println(check.Verify(presented, time.Now()) == nil)

// A token that is not ours does not verify.
fmt.Println(check.Verify("AAAAAAAAAAAAAAAAAAAAAAAAAA", time.Now()) == nil)
Output:
true
false

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidToken = errors.New("auth: invalid token")

ErrInvalidToken is returned when a token is malformed, unknown or expired.

One error for all three, deliberately: distinguishing "no such token" from "expired token" tells an attacker which of their guesses existed.

Functions

func FromAuthorizationHeader

func FromAuthorizationHeader(header string) (string, error)

FromAuthorizationHeader extracts a bearer token.

It rejects anything that is not exactly "Bearer <token>" of the right length, rather than accepting a prefix match. A header of "Bearer" with no value, or with extra fields after the token, is a malformed request rather than something to interpret generously.

func HashToken

func HashToken(plain string) []byte

HashToken returns the stored form of a plaintext token.

SHA-256 rather than bcrypt, and that is not an oversight. A password is low-entropy and chosen by a human, so it needs a slow hash to survive being guessed. A token is 128 bits from crypto/rand, so there is nothing to guess, and a slow hash would only mean every authenticated API request pays for a key-derivation function.

Types

type Token

type Token struct {
	// PlainText is populated only by NewToken. It is never read back from
	// storage, because storage never has it.
	//
	// v0.7.0 fixed exactly this: tokens were persisted in plaintext and
	// serialised into JSON responses, so a database read or a logged response
	// body handed over working credentials.
	PlainText string `json:"-"`

	// Hash is what gets stored and compared.
	Hash []byte `json:"-"`

	Expiry time.Time `json:"expiry"`
}

Token is an API token: a plaintext value shown to the user exactly once, and a hash that is all the server retains.

func NewToken

func NewToken(ttl time.Duration) (*Token, error)

NewToken mints a token valid for ttl.

The plaintext exists only in the returned value. Show it to the user once and store the hash; there is no way to recover it afterwards, which is the point.

func (*Token) Verify

func (t *Token) Verify(plain string, now time.Time) error

Verify reports whether plain matches this token and has not expired.

Constant-time comparison: one that returns on the first differing byte leaks the stored hash to anyone willing to measure, one byte at a time.

Jump to

Keyboard shortcuts

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