diff --git a/internal/auth/auth.go b/internal/auth/auth.go new file mode 100644 index 0000000..f4dcd11 --- /dev/null +++ b/internal/auth/auth.go @@ -0,0 +1,38 @@ +package auth + +import ( + "context" + "net" +) + +// TenantID identifies a tenant in a multi-tenant deployment. +// The zero value ("") is the [Anonymous] sentinel used in single-tenant mode. +type TenantID string + +// Anonymous is the sentinel TenantID used when authentication is disabled +// (single-tenant / no-op mode). All call-sites pass this value until a real +// Authenticator is wired in. +const Anonymous TenantID = "" + +// Authenticator authenticates an incoming connection and returns the +// TenantID it belongs to. Implementations must be safe for concurrent use. +// +// A nil error with [Anonymous] means the connection is accepted in +// non-multi-tenant mode. Any non-nil error must be treated as a rejection +// and the caller must close conn. +type Authenticator interface { + Authenticate(ctx context.Context, conn net.Conn) (TenantID, error) +} + +// NopAuthenticator is a no-op Authenticator that always returns [Anonymous]. +// It is the default when multi-tenant support is not configured. +type NopAuthenticator struct{} + +// compile-time interface check. +var _ Authenticator = NopAuthenticator{} + +// Authenticate implements [Authenticator]. It always succeeds and returns +// [Anonymous] without inspecting conn or ctx. +func (NopAuthenticator) Authenticate(_ context.Context, _ net.Conn) (TenantID, error) { + return Anonymous, nil +} diff --git a/internal/auth/doc.go b/internal/auth/doc.go new file mode 100644 index 0000000..cd9c0e5 --- /dev/null +++ b/internal/auth/doc.go @@ -0,0 +1,23 @@ +// Package auth defines the Authenticator interface and tenant identity types +// for multi-tenant support. +// +// # Current state +// +// Only the scaffold is present. The [NopAuthenticator] is used everywhere and +// returns [Anonymous], preserving existing single-tenant behaviour. +// +// # Planned multi-tenant flow +// +// 1. A real Authenticator (e.g. HMACAuthenticator backed by SQLite) reads a +// short token from the first bytes of conn, validates it against a per-tenant +// shared secret, and returns the matching [TenantID]. +// 2. bondserver.Registry uses the TenantID as part of its de-duplication key so +// ConnIDs from different tenants never collide. +// 3. tcpfwdserver and udpserver resolve connectAddr per-tenant, allowing each +// tenant to target a different backend. +// 4. Deep-link provisioning delivers per-tenant secrets to mobile clients without +// manual configuration. +// +// None of the items above are implemented yet. See notes/AUTH_PLAN.md for the +// detailed design. +package auth