identity

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 4 Imported by: 13

README

identity

Tenant-scoped identity context for Go multi-tenant services.

identity provides an immutable, validated Context type that travels through context.Context, giving every layer of your application access to who is calling, which tenant they belong to, and what they can do.

Install

go get github.com/laenen-partners/identity

Quick start

Create an identity
id, err := identity.New(
    "tenant_abc",           // tenant ID
    "ws_prod",              // workspace ID
    "usr_42",               // principal ID
    identity.PrincipalUser, // "user" or "service"
    []string{"admin", "editor"},
)
if err != nil {
    // handles missing fields or unknown principal type
}
Propagate through context
// Attach to context (typically in auth middleware)
ctx = identity.WithContext(ctx, id)

// Retrieve downstream
id, ok := identity.FromContext(ctx)

// Or panic if absent — use only behind auth middleware
id := identity.MustFromContext(ctx)
Check roles
if id.HasRole("admin") {
    // ...
}

if id.HasAnyRole("admin", "editor") {
    // ...
}
Structured logging

Context implements String() and LogValue() for clean output with slog:

slog.Info("request authorized", "identity", id)
// => identity.tenant_id=tenant_abc identity.workspace_id=ws_prod ...

API overview

Method Description
New(...) Validated constructor; returns error on invalid input
TenantID() Tenant the caller belongs to
WorkspaceID() Workspace within the tenant
PrincipalID() User or service account identifier
PrincipalType() PrincipalUser or PrincipalService
Roles() Copy of the caller's role list
HasRole(role) Check for a single role
HasAnyRole(roles...) Check for at least one of the given roles
IsUser() / IsService() Convenience type checks
WithContext(ctx, id) Store identity in context.Context
FromContext(ctx) Retrieve identity (returns ok bool)
MustFromContext(ctx) Retrieve identity or panic

Design decisions

  • Immutable after construction. Fields are unexported with read-only accessors. Role slices are defensively copied on input and output. This prevents accidental or malicious mutation of security-critical data.
  • Validated at the boundary. New rejects empty IDs and unknown principal types so downstream code can trust the values without re-checking.
  • Zero dependencies. Standard library only.

Testing

go test -v -count=1 ./...

License

MIT

Documentation

Overview

Package identity provides tenant-scoped identity context for multi-tenant services.

Every authenticated request carries an identity Context describing who is calling, which tenant and workspace they belong to, and what roles they hold. Use the context helpers to propagate and retrieve this information through context.Context.

ctx = identity.WithContext(ctx, id)
id, ok := identity.FromContext(ctx)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func WithContext

func WithContext(ctx context.Context, id Context) context.Context

WithContext returns a new context carrying the given identity.

Types

type Context

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

Context carries the authenticated caller's identity within a tenant.

All fields are read-only after construction; create instances via New.

func FromContext

func FromContext(ctx context.Context) (Context, bool)

FromContext extracts the identity from ctx. Returns the zero Context and false if none is present.

func MustFromContext

func MustFromContext(ctx context.Context) Context

MustFromContext extracts the identity from ctx, panicking if absent. Use only in code paths where middleware guarantees the identity exists.

func New

func New(tenantID, workspaceID, principalID string, principalType PrincipalType, issuer string, roles []string) (Context, error)

New creates a validated identity Context. Returns an error if any required field is empty or principalType is not a known value.

func (Context) HasAnyRole

func (c Context) HasAnyRole(roles ...string) bool

HasAnyRole reports whether the identity holds at least one of the given roles.

func (Context) HasRole

func (c Context) HasRole(role string) bool

HasRole reports whether the identity holds the given role.

func (Context) IsService

func (c Context) IsService() bool

IsService is a convenience check for PrincipalService.

func (Context) IsUser

func (c Context) IsUser() bool

IsUser is a convenience check for PrincipalUser.

func (Context) Issuer added in v0.2.0

func (c Context) Issuer() string

func (Context) LogValue

func (c Context) LogValue() map[string]any

LogValue implements slog.LogValuer so the identity renders cleanly in structured log output.

func (Context) PrincipalID

func (c Context) PrincipalID() string

func (Context) PrincipalType

func (c Context) PrincipalType() PrincipalType

func (Context) Roles

func (c Context) Roles() []string

Roles returns a copy of the role list.

func (Context) String

func (c Context) String() string

String returns a human-readable representation useful for logging. Never includes secret material.

func (Context) TenantID

func (c Context) TenantID() string

func (Context) WorkspaceID

func (c Context) WorkspaceID() string

type PrincipalType

type PrincipalType string

PrincipalType distinguishes between human users and service accounts.

const (
	PrincipalUser    PrincipalType = "user"
	PrincipalService PrincipalType = "service"
)

Jump to

Keyboard shortcuts

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