package rbac import "context" // Identity represents the authenticated principal for a request. // // It is a value type — always copied, never a pointer — to prevent nil-check // burden and avoid accidental mutation of a shared context value. // // Construction follows a two-step pattern: // 1. [NewIdentity] populates authentication data from the token (uid, name, email). // 2. [Identity.WithTenant] optionally enriches with a tenant ID in a later // middleware step, returning a new value without mutating the original. type Identity struct { UID string TenantID string DisplayName string Email string } // authContextKey is an unexported type used as the context key for Identity. // Using a private type prevents collisions with keys from other packages. type authContextKey struct{} var authKey = authContextKey{} // NewIdentity creates an Identity from token authentication data. // TenantID is intentionally left empty — populate it later with [Identity.WithTenant] // once the enrichment middleware has resolved it. func NewIdentity(uid, displayName, email string) Identity { return Identity{ UID: uid, DisplayName: displayName, Email: email, } } // WithTenant returns a copy of the Identity with TenantID set to id. // The receiver is not mutated — safe to call from concurrent middleware. func (i Identity) WithTenant(id string) Identity { i.TenantID = id return i } // SetInContext stores the Identity in ctx and returns the enriched context. func SetInContext(ctx context.Context, id Identity) context.Context { return context.WithValue(ctx, authKey, id) } // FromContext retrieves the Identity stored by [SetInContext]. // Returns the zero-value Identity and false if no identity is present. func FromContext(ctx context.Context) (Identity, bool) { id, ok := ctx.Value(authKey).(Identity) return id, ok }