authtest

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 35 Imported by: 0

Documentation

Overview

Package authtest runs AuthKit in a host's Go tests: a real Client on a scratch PostgreSQL schema, an Outbox that captures every email and SMS, and helpers for the usual setup (a verified user, a signed-in session, a role, an authenticator app, a device key, a replica, a stale session).

auth, outbox := authtest.New(t, authtest.WithConfig(func(c *authkit.Config) {
	c.Roles = myapp.Roles()
}))
alice := authtest.NewUser(t, auth)
authtest.GrantRole(t, auth, iam.RootGroup(), iam.UserSubject(alice.ID), myapp.Admin)
tokens := authtest.SignIn(t, auth, alice)
// call the host's handlers with "Bearer "+tokens.AccessToken, or drive
// auth.Handler() and read codes and links from outbox.

New needs AUTHKIT_TEST_DATABASE_URL, a database where the test may create schemas. Without it the test is skipped, or fails when AUTHKIT_TEST_REQUIRE_DB=1. AUTHKIT_TEST_KEEP_DB=1 keeps each schema.

The package is outside AuthKit's compatibility contract: it may change in any minor release.

Index

Constants

View Source
const (
	Issuer   = "https://example.com"
	Audience = "authtest"
)

Issuer and Audience are the token issuer and audience New configures.

View Source
const Password = "Authtest-password-1"

Password is the password NewUser gives every account.

Variables

This section is empty.

Functions

func GrantRole added in v0.147.0

func GrantRole(t testing.TB, auth *authkit.Client, group iam.GroupRef, subject iam.Subject, role iam.Role)

GrantRole gives subject role in group with system authority. A role that requires MFA needs the account's second factor first (EnrollTOTP).

func Replica added in v0.147.0

func Replica(t testing.TB, auth *authkit.Client, opts ...Option) *authkit.Client

Replica builds another Client on auth's database and schema, as another replica of the deployment runs: the Config and Deps auth was built with (its Outbox included, when New built it), then opts. auth may be any Client authkit.New built, a host's own included. A different Token.Issuer makes a sibling deployment sharing the account store; different HTTPConfig serves the same accounts another way. HTTP and Password are copied, so opts may set their fields; replace, never mutate, the maps and slices opts change: the replica shares auth's.

func RevokeRole added in v0.147.0

func RevokeRole(t testing.TB, auth *authkit.Client, group iam.GroupRef, subject iam.Subject, role iam.Role)

RevokeRole takes role in group from subject with system authority.

func SignIn added in v0.147.0

func SignIn(t testing.TB, auth *authkit.Client, u User) iam.TokenSet

SignIn signs u in with its password through auth's HTTP surface and follows the AuthResult to a session: a second factor is answered with u.TOTP (or the account's remembered app, TOTPOf); an enrollment the deployment requires adds an authenticator app with the enrollment token, which SignIn remembers for the account's later sign-ins. It returns the session's tokens.

func StaleSession added in v0.147.0

func StaleSession(t testing.TB, auth *authkit.Client, accessToken string) string

StaleSession moves the sign-in of the session behind accessToken a day into the past, as if its user signed in long ago, and returns a new access token for that session: routes that need a recent sign-in then ask it for a step-up. auth may be any Client authkit.New built.

func TOTPCode added in v0.147.0

func TOTPCode(t testing.TB, secret string, at time.Time) string

TOTPCode is the RFC 6238 code of secret (base32) at the given time: SHA-1, six digits, 30-second steps.

Types

type DeviceKey added in v0.147.0

type DeviceKey struct {
	ID  string
	Key ed25519.PrivateKey
	// AccessToken is the enrollment's sign-in.
	AccessToken string
}

DeviceKey is a device key enrolled on an account (see package devicekey).

func EnrollDeviceKey added in v0.147.0

func EnrollDeviceKey(t testing.TB, auth *authkit.Client, outbox *Outbox, u User) DeviceKey

EnrollDeviceKey enrolls a new Ed25519 device key on u with the devicekey client, reading the emailed code from outbox and answering a second factor with u.TOTP. The Client needs Config.DeviceKeys.Enabled.

type Message added in v0.147.0

type Message = testoutbox.Message

Message is one captured email or SMS: Channel ("email" or "sms"), Kind, To, Language and what it carries (Code, Link and the Link's Token, the verification Purpose, a ContactChange or DeviceKey notice).

type Option added in v0.147.0

type Option func(*setup)

Option adjusts what New builds.

func WithConfig added in v0.147.0

func WithConfig(fn func(*authkit.Config)) Option

WithConfig edits the Config New passes to authkit.New, after its defaults.

func WithDeps added in v0.147.0

func WithDeps(fn func(*authkit.Deps)) Option

WithDeps edits the Deps New passes to authkit.New. The Outbox's senders are already Email and SMS; set Postgres to use a pool of your own instead of AUTHKIT_TEST_DATABASE_URL.

type Outbox added in v0.147.0

type Outbox = testoutbox.Outbox

Outbox captures every email and SMS AuthKit sends, in order, so a test can complete sign-up, verification, reset and sign-in flows. New wires one; for a Client built another way, pass Email() and SMS() as Deps.Email and Deps.SMS. Its methods:

  • Last(t, kind, to) Message: the newest message of kind to that address or number ("" = anyone); the test fails when there is none.
  • Messages(kind, to) []Message: all of them, oldest first ("" = any).
  • Email() authkit.EmailSender, SMS() authkit.SMSSender: the senders.
  • SetEmailHealth(err), SetSMSHealth(err): what the senders' CheckHealth returns (nil, the default, is healthy), so a test can take a channel down and bring it back.

For example, out.Last(t, iam.MessageVerification, email).Code.

func New added in v0.147.0

func New(t testing.TB, opts ...Option) (*authkit.Client, *Outbox)

New migrates AuthKit into a fresh schema, builds a Client on it, and returns the Client with the Outbox wired as its email and SMS senders. The defaults differ from a zero Config only where a test needs them to:

  • Token: Issuer and Audience.
  • TwoFactor.TOTPSecretKey: random, so authenticator apps can enroll.
  • HTTP: served (DirectPeerIP), without rate limits (Deps.Limiter).
  • Deps.KeySource: an RSA key generated once per test binary.
  • Schema and River.Schema: the scratch schema, unless set.

The Client is not started: call Start when a test needs River's work, such as Deps.OnEvent or the deletion hooks. Cleanup closes the Client and drops the scratch schema.

type TOTP added in v0.147.0

type TOTP struct {
	Secret string
	// contains filtered or unexported fields
}

TOTP is an authenticator app enrolled on an account.

func EnrollTOTP added in v0.147.0

func EnrollTOTP(t testing.TB, auth *authkit.Client, u User) *TOTP

EnrollTOTP signs u in and adds an authenticator app through auth's HTTP surface, as a user would. SignIn uses it from now on; keeping it as u.TOTP works too.

func TOTPOf added in v0.149.0

func TOTPOf(userID string) *TOTP

TOTPOf is the authenticator app EnrollTOTP or SignIn enrolled on the account userID; nil for none.

func (*TOTP) Code added in v0.147.0

func (a *TOTP) Code(t testing.TB) string

Code returns a code AuthKit has not accepted yet. AuthKit takes each 30s step once, and at most one step ahead, so Code waits for the clock when both usable steps are spent.

type TestIssuer

type TestIssuer struct {
	// contains filtered or unexported fields
}

TestIssuer is a stand-in token issuer with a JWKS endpoint, for testing a service that only verifies tokens (verify.Verifier) without running AuthKit.

func NewTestIssuer

func NewTestIssuer() *TestIssuer

NewTestIssuer creates a new test issuer with an RSA key pair. Register its verifier entry with IsLocal only when modeling local user IDs. Otherwise access tokens expose the qualified external Issuer and Subject.

func NewTestIssuerWithAudience

func NewTestIssuerWithAudience(audience string) *TestIssuer

NewTestIssuerWithAudience creates a test issuer with a specific audience claim.

func NewTestIssuerWithSigner

func NewTestIssuerWithSigner(signer keys.Signer, audience string) *TestIssuer

NewTestIssuerWithSigner creates a test issuer using any keys.Signer (RSA, EC, Ed25519).

func (*TestIssuer) Audience

func (ti *TestIssuer) Audience() string

func (*TestIssuer) Close

func (ti *TestIssuer) Close()

func (*TestIssuer) CreateExpiredToken

func (ti *TestIssuer) CreateExpiredToken(userID, email string) string

func (*TestIssuer) CreateToken

func (ti *TestIssuer) CreateToken(userID, email string) string

func (*TestIssuer) CreateTokenWithClaims

func (ti *TestIssuer) CreateTokenWithClaims(userID, email string, extraClaims map[string]any) string

func (*TestIssuer) CreateTokenWithExpiry

func (ti *TestIssuer) CreateTokenWithExpiry(userID, email string, expiry time.Time) string

func (*TestIssuer) Signer

func (ti *TestIssuer) Signer() keys.Signer

func (*TestIssuer) URL

func (ti *TestIssuer) URL() string

type User added in v0.147.0

type User struct {
	iam.User
	Email    string
	Password string
	// TOTP is the authenticator app SignIn answers a second-factor challenge
	// with. Unset, SignIn uses the app EnrollTOTP or an earlier SignIn enrolled
	// on the account (TOTPOf).
	TOTP *TOTP
}

User is an account and what SignIn needs to sign it in. Email is the account's address as text ("" for none).

func NewUser added in v0.147.0

func NewUser(t testing.TB, auth *authkit.Client) User

NewUser creates an account with a verified email, a username and Password, as the host's own code would (Client.CreateUser). The username (user plus 16 random hex digits) and email are random, so test processes sharing one schema never collide.

Jump to

Keyboard shortcuts

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