// Package auth provides authentication and authorization primitives for the // Cyrene v2 gateway: JWT signing/verification (HS256), an RBAC middleware built // on an owner>admin>viewer role hierarchy, in-memory API keys, and salted // password hashing. All APIs are self-contained and use only the standard // library, so the package compiles without adding dependencies to the module. // // Identity is carried as strong-typed actor metadata and decided by policy (see // DevDocs/design/11): it is never inferred by an LLM. This package supplies the // credential layer that such policies rely on. package auth import ( "errors" "fmt" ) // Role is an authorization role in the owner>admin>viewer hierarchy. type Role string const ( // RoleOwner has full authority, including security/key/system settings. RoleOwner Role = "owner" // RoleAdmin manages modules but no sensitive security items. RoleAdmin Role = "admin" // RoleViewer is read-only (dashboard/logs/monitoring). RoleViewer Role = "viewer" ) // Sentinel errors returned by the auth package. var ( // ErrInvalidSecret reports a JWTSecret shorter than the required minimum. ErrInvalidSecret = errors.New("auth: JWTSecret must be at least 32 characters") // ErrInvalidRole reports an unknown or unparseable role name. ErrInvalidRole = errors.New("auth: invalid role") // ErrMalformedToken reports a token that is not well-formed. ErrMalformedToken = errors.New("auth: malformed token") // ErrInvalidSignature reports a token whose HMAC signature does not verify. ErrInvalidSignature = errors.New("auth: invalid signature") // ErrTokenExpired reports a token whose exp is in the past. ErrTokenExpired = errors.New("auth: token expired") // ErrUnsupportedAlg reports a token declaring an algorithm other than HS256. ErrUnsupportedAlg = errors.New("auth: unsupported algorithm") // ErrUnauthorized reports a missing/invalid/unexpired-failed credential. ErrUnauthorized = errors.New("auth: unauthorized") // ErrForbidden reports a valid credential with insufficient privilege. ErrForbidden = errors.New("auth: forbidden") ) // Package-wide constants. `source` is the log provenance tag used by every // logger.WithSource call in this package (no bare logs are emitted). const ( source = "auth" minSecretLen = 32 keyLength = 32 passwordScheme = "sha256" saltLength = 16 passwordIterations = 100000 ) // ParseRole converts a string into a Role, erroring on unknown values. func ParseRole(s string) (Role, error) { r := Role(s) if !r.Valid() { return "", fmt.Errorf("%w: %q", ErrInvalidRole, s) } return r, nil } // Valid reports whether r is one of owner, admin, or viewer. func (r Role) Valid() bool { return r.rank() > 0 } // rank returns the privilege rank of r. Invalid roles rank below viewer. func (r Role) rank() int { switch r { case RoleOwner: return 3 case RoleAdmin: return 2 case RoleViewer: return 1 default: return 0 } } // HasRole reports whether role holds at least the privilege of required, where // owner>admin>viewer. Owner satisfies admin and viewer, admin satisfies viewer, // and viewer satisfies only viewer. func HasRole(role, required Role) bool { return role.rank() >= required.rank() }