studioauth

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ModeSelf     = "self"
	ModeElevated = "elevated"
)

Who a Studio data request acts as.

Studio used to forward every request with the service role key, which bypasses RLS entirely: being admitted to the panel was the same thing as having unrestricted access to the database. Identity and capability are separate, so the acting identity is now separate too.

  • ModeSelf forwards the caller's own token. PostgREST assumes their role and their own RLS policies apply, so Studio shows them exactly what their application would.
  • ModeElevated injects the service role key. It is the escape hatch for administration that RLS would otherwise block, gated on the `elevatedSql` capability and recorded.

A caller may always ask to drop to ModeSelf. Only a role holding `elevatedSql` may act elevated, and asking for it without that capability is refused rather than silently downgraded — a request that would return a misleadingly empty result is worse than an error.

View Source
const ActingModeHeader = "X-Supatype-Acting-Mode"

ActingModeHeader lets the client state which identity it wants, and carries the resolved mode back on the response so the UI can show it. Studio's elevated banner reads this rather than assuming.

View Source
const RoleAdmin = "admin"

RoleAdmin is the only role that can grant or revoke Studio access.

Variables

View Source
var DefaultAdminRoles = []string{"admin", "supatype_admin"}

DefaultAdminRoles are allowed Studio admin roles when config/env omit overrides.

Functions

func AdminRolesFromConfigFile

func AdminRolesFromConfigFile(path string) []string

AdminRolesFromConfigFile merges adminRoles from admin-config.json when present.

func AdminRolesFromEnv

func AdminRolesFromEnv() []string

AdminRolesFromEnv reads STUDIO_ADMIN_ROLES (comma-separated) or returns defaults.

func AllowsRequest added in v1.4.0

func AllowsRequest(perms StudioPermissions, method, path string) bool

AllowsRequest reports whether a capability set covers one proxied request.

Mirrors the control plane's `resolvePermission` mapping so a role means the same thing in both hosts. Until Studio stops proxying through the service role, this is the only thing standing between an `editor` membership and a schema change.

func DevBypass

func DevBypass() bool

DevBypass is true when local open Studio is explicitly enabled (supatype dev docker only).

func KnownStudioRoles added in v1.4.0

func KnownStudioRoles() []string

KnownStudioRoles lists the roles a membership row may hold, most privileged first, so the API response and CLI help are stably ordered.

func MembersAPI added in v1.4.0

func MembersAPI(c Config) http.Handler

MembersAPI serves the Studio membership assignment API:

GET    /admin/studio-roles              — the role catalogue and its matrix
GET    /admin/studio-members            — current grants
PATCH  /admin/studio-members/{userId}   — set a role  {"role": "editor"}
DELETE /admin/studio-members/{userId}   — revoke

Writes `_supatype.studio_members` and nothing else — never `app_metadata`, which belongs to the developer's own application roles.

Every request re-resolves the caller's capability from the database. A session payload or cookie would let a demoted admin keep administering until it expired, and capability is exactly the thing that must not be cacheable.

func ProxyHandler

func ProxyHandler(inner http.Handler, c Config) http.Handler

ProxyHandler forwards privileged API calls after admin JWT verification. inner must be the main service mux (without /studio routes).

func ReadAdminConfigFile

func ReadAdminConfigFile(path string) ([]byte, error)

ReadAdminConfigFile reads admin-config.json from a relative path under the working directory.

func RequireAdmin

func RequireAdmin(c Config, next http.Handler) http.Handler

RequireAdmin wraps a handler with studio admin JWT checks (skipped when DevBypass).

func SchemaHandler added in v1.4.0

func SchemaHandler(c Config) http.HandlerFunc

SchemaHandler serves GET /studio/schema.

func SessionHandler added in v1.4.0

func SessionHandler(c Config) http.HandlerFunc

SessionHandler serves GET /studio/session.

Deliberately has no `userId` parameter: it answers for the caller the token names and nobody else. A parameter would be an invitation to ask about another user, and the answer would then have to be trusted by whoever asked.

func VerifyHandler

func VerifyHandler(c Config) http.HandlerFunc

VerifyHandler serves GET /studio/auth/verify.

Types

type Config

type Config struct {
	JWTSecret      string
	ServiceRoleKey string
	// AnonKey is sent as `apikey` when a request acts as the caller rather than
	// elevated, so gateways that require a key still see one that carries no
	// privilege of its own.
	AnonKey    string
	AdminRoles []string
	Mode       string
	// StudioRole resolves a verified user id to their Studio role from
	// `_supatype.studio_members`. Studio capability deliberately does not come
	// from a JWT claim: `app_metadata` is the developer's namespace for their own
	// app roles, so reading it here means assigning an app role can hand out admin
	// UI access by accident. Nil keeps the legacy claim-based path, so a
	// deployment that has not been migrated still works.
	StudioRole StudioRoleLookup
}

Config holds studio auth handler dependencies.

func ConfigFromServer

func ConfigFromServer(cfg *serverconf.ServerConfig) Config

ConfigFromServer builds handler config from ServerConfig and admin-config path.

type Result

type Result struct {
	Allowed bool
	Message string
	Role    string
	Sub     string
	// Permissions is set when capability came from a membership role. Nil on the
	// legacy claim path, where the claim only ever meant "is an admin".
	Permissions *StudioPermissions
}

Result is the outcome of an admin access check.

func ResolveAccess added in v1.4.0

func ResolveAccess(req *http.Request, c Config) Result

ResolveAccess decides Studio access for a request.

The token establishes *identity* (signature, expiry, subject). Capability comes from `_supatype.studio_members` when a lookup is configured, so a user with no membership row is refused even if their claims say "admin", and a member is admitted without needing any claim at all.

func VerifyBearerToken

func VerifyBearerToken(token string, jwtSecret string, adminRoles []string) Result

VerifyBearerToken validates a user JWT and checks studio admin role membership. Role resolution: app_metadata.role first, then top-level role when it is an admin role.

func VerifyRequest

func VerifyRequest(req *http.Request, jwtSecret string, adminRoles []string) Result

VerifyRequest extracts Bearer token from req and verifies it.

type StudioPermissions added in v1.4.0

type StudioPermissions struct {
	// Read and Write cover content rows — the data plane.
	Read  bool `json:"read"`
	Write bool `json:"write"`
	// SchemaView covers the schema and migration history views.
	SchemaView bool `json:"schemaView"`
	// SQLEditor runs queries as the acting user's own role, so RLS applies to
	// them. This is what makes the editor safe to hand to a contractor: they can
	// explore and debug without being able to dump the database.
	SQLEditor bool `json:"sqlEditor"`
	// ElevatedSQL covers DDL and anything that bypasses RLS.
	ElevatedSQL bool `json:"elevatedSql"`
	// ManageMembers covers Studio membership and project API keys.
	ManageMembers bool `json:"manageMembers"`
}

StudioPermissions is the capability set Studio renders from.

func PermissionsForRole added in v1.4.0

func PermissionsForRole(role string) (StudioPermissions, bool)

PermissionsForRole returns the capability set for a Studio role, and whether the role is one this deployment understands.

type StudioRoleLookup added in v1.4.0

type StudioRoleLookup func(userID string) (string, bool)

StudioRoleLookup returns the Studio role recorded for a user id. The second result is false when the user has no membership row.

Jump to

Keyboard shortcuts

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