package jwtauth import "context" type Claims struct { UserID string Groups []string // JTI and Scopes are populated only for HS256 shortcut tokens; a full OIDC / // dev session leaves them empty. A non-empty JTI marks a *scoped* token — // one allowed to reach only the endpoints its Scopes grant (the route layer // enforces this). This keeps a leaked shortcut token's blast radius minimal. JTI string Scopes []string // SessionScope is set only for cdrop self-signed session tokens (scan-login): // "full" or "guest". Empty for OIDC / dev sessions and scoped shortcut tokens. // A "guest" session is capability-limited (requireFullSession rejects it on // account-management routes) even though it is not Scoped(). SessionScope string } // Scoped reports whether these claims came from a scoped shortcut token rather // than a full login session. func (c *Claims) Scoped() bool { return c.JTI != "" } // Guest reports whether these claims came from a restricted guest session (a // scan-login borrow). Guest sessions can transfer files but not manage the // account, approve other devices, or mint long-lived tokens. func (c *Claims) Guest() bool { return c.SessionScope == "guest" } // HasScope reports whether the claims grant the named scope. func (c *Claims) HasScope(scope string) bool { for _, s := range c.Scopes { if s == scope { return true } } return false } type ctxKey int const ( claimsCtxKey ctxKey = iota deviceCtxKey deviceTypeCtxKey ) func ClaimsFromContext(ctx context.Context) (*Claims, bool) { c, ok := ctx.Value(claimsCtxKey).(*Claims) return c, ok } // ContextWithClaims attaches claims to a context — the inverse of // ClaimsFromContext. The auth middleware uses this; it is also the seam handlers // and tests use to inject claims directly. func ContextWithClaims(ctx context.Context, c *Claims) context.Context { return context.WithValue(ctx, claimsCtxKey, c) } func DeviceNameFromContext(ctx context.Context) (string, bool) { n, ok := ctx.Value(deviceCtxKey).(string) return n, ok } // DeviceTypeFromContext returns the client-declared device type set by the auth // middleware (browser / macos / windows / linux), defaulting to "browser". func DeviceTypeFromContext(ctx context.Context) string { t, ok := ctx.Value(deviceTypeCtxKey).(string) if !ok || t == "" { return "browser" } return t }