@krak-stack/auth
v0.2.27
Published
Effect HttpApi clients and schemas for KrakStack Auth.
Readme
@krak-stack/auth
Typed Effect contracts, server authentication middleware, project authorization primitives, and React authentication components for Krakstack Auth.
Install
bun add @krak-stack/auth effectEffect forms
React components use @lucas-barake/[email protected], which supports
stable Effect 4 without compatibility patches. When upgrading from the patched
betas, remove their patchedDependencies entries and patch files, update any
direct form dependencies or version overrides to 1.1.0, and run bun install.
Server-only imports do not load the form libraries.
Entry points
Choose a narrow package subpath for the runtime boundary:
| Entry point | Purpose |
| -------------------------------- | --------------------------------------------------------------------- |
| @krak-stack/auth/server | Proxy, AuthMiddleware, AuthService, ActorRequired, and policies |
| @krak-stack/auth/components | React provider, forms, account controls, and UI permission helpers |
| @krak-stack/auth/access | Typed project access definitions |
| @krak-stack/auth/access/matrix | Generated permission matrix |
| @krak-stack/auth/api | Combined Effect HttpApi contracts |
| @krak-stack/auth/auth | Browser authentication HttpApi group and schemas |
| @krak-stack/auth/schema | Shared schemas |
| @krak-stack/auth/admin | Administrative API contract |
| @krak-stack/auth/extra | Browser-facing extra API contract |
The complete export map is defined in package.json and documented at auth.krakstack.net/en/docs/sdk.
Server Configuration
Server clients use:
KRAKSTACK_AUTH_URL=https://auth.example.com
KRAKSTACK_AUTH_SERVICE_API_KEY=server_only_secretAuthClientConfig accepts explicit baseUrl and redacted apiKey overrides. Never expose the service key through a VITE_* variable.
import { AuthService } from "@krak-stack/auth/server";
import { Effect } from "effect";
const program = Effect.gen(function* () {
const auth = yield* AuthService;
return yield* auth.requireUserOrganization();
});Use AuthMiddleware.layer() to provide request headers and configuration in an Effect HttpApi application. See the middleware guide.
AuthMiddleware also annotates request logs and spans with verified user,
organization, and actor IDs using its existing session lookup. Missing sessions
are anonymous; auth service failures are not silently treated as anonymous.
ActorRequired remains responsible for project authorization and CurrentActor.
For plain HttpRouter routes, use the same authentication through
AuthMiddleware.routerLayer(). For example, browser Events ingestion can inherit
verified identity without an Events.layer({ identity }) callback:
import { AuthMiddleware } from "@krak-stack/auth/server";
import { HttpApiOtlp } from "@krak-stack/registry/opentelemetry/api";
import { Events } from "@krak-stack/registry/events";
import { Layer } from "effect";
const telemetry = HttpApiOtlp.layer.pipe(
Layer.provide(AuthMiddleware.routerLayer({ paths: ["/api/otel/v1/logs"] })),
Layer.provide(Events.ingestionLayer),
Layer.provide(eventsLayer),
);Here eventsLayer is the configured Events layer, with SQL provided when
persistence is needed. paths limits auth lookup to the listed URL pathnames;
without it, the middleware covers every route it is provided to. The router
adapter uses the same auth configuration and organization-impersonation policy
as AuthMiddleware.layer(). It does not grant permissions or provide CurrentActor.
React Components
The component bundle uses the optional peer dependencies declared in package.json and Krakstack/shadcn UI primitives from @krak-stack/registry. Install the component peers used by your application; server-only consumers only need effect.
When components are configured, register their Tailwind source:
@import "tailwindcss";
@import "@krak-stack/auth/tailwind.css";The React components use the exported Effect query atoms and the owned authentication HTTP group. Browser requests include credentials so hosted components can resolve sessions across configured origins.
UserButton accepts menuActions for app-owned menu items such as dashboard or workspace links:
import { UserButton } from "@krak-stack/auth/components";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
<UserButton
menuActions={
<DropdownMenuItem render={<a href="/dashboard" />}>
Dashboard
</DropdownMenuItem>
}
signOutRedirect="/"
/>;See React components and CSS for the provider and component requirements.
Locale syncing
Nest KrakstackAuthProvider inside KrakstackProvider. The auth provider reads
the registry locale context and saves it to the signed-in user's locale when
it differs. It does not accept a locale prop or restore the interface language
from the saved account preference. Use LocaleSwitcher directly from
@krak-stack/registry/locale-switcher with your application's language-change
callback; no auth-specific selector wrapper is needed.
The context is authoritative, including after a Paraglide reload. Public pages
outside the auth provider change only the browser locale; pages inside it can
update the account even when the language comes from a localized URL. Signed-out
visitors do not trigger user updates. An optional onLocaleSyncError callback
lets the application display a localized save error without a retry loop.
Existing consumers already nesting KrakstackAuthProvider inside
KrakstackProvider only need an SDK version update after the auth server is
deployed with locale support. No consumer database migration, new provider prop,
or selector change is required. Impersonated sessions do not sync preferences.
Krakstack Auth emails use the recipient's saved language, falling back to the request locale and then English when no valid saved preference exists. See locale syncing for setup, lifecycle behavior, and background-notification guidance.
Authorization
Define project permissions with defineProjectAccess, resolve actors with ActorRequired, and enforce backend policies with withPolicy. Frontend permission helpers control presentation only.
AuthMiddleware annotates the current span, child spans and handler logs with verified attribution:
| Attribute | User session | User-owned API key | Organization-owned API key |
| -------------------------- | -------------------------------------- | -------------------------- | -------------------------- |
| app.actor.type | user | user | organization |
| app.actor.id | User ID | Owner user ID | Owner organization ID |
| app.auth.method | session | api_key | api_key |
| app.auth.credential.id | Omitted | API-key record ID | API-key record ID |
| app.auth.impersonator.id | Initiating admin ID when impersonating | Omitted | Omitted |
| user.id | User ID | Owner user ID | Omitted |
| user.roles | Organization roles | Owner's organization roles | Empty array |
| organization.id | Active organization ID | Active organization ID | Owner organization ID |
user.id and user.roles follow OpenTelemetry user semantic conventions. Roles are added by ActorRequired, not session-only router middleware. Names, photos, email addresses, API-key values, and session tokens are not emitted. OTel defines user.name as login/username and user.full_name as full name; neither is needed for attribution here. The app.actor.*, app.auth.* and existing organization.id fields are application conventions. The public authorization model still uses type: "apiKey", but telemetry identifies its owning user or organization as the responsible principal and records the key separately as a credential. This replaces the previous app.actor.owner.* fields and API-key actor IDs; telemetry consumers must migrate together with the SDK. Audit consumers resolve current names/photos from organization membership for display, rather than copying them into logs. MCP and AI execution context belongs to the respective integration layers and does not replace the authenticated actor.
Auth-owned user, organization and actor annotations replace the inherited set, preventing stale user/API-key owner attribution. Unrelated request, MCP and agent context is preserved, and scoped log/child-span context is restored afterward. Stale fields on an already-created current span are cleared to empty strings because the tracer interface has no attribute-removal method; handler logs and new child spans omit those fields entirely.
ActorRequired resolves the project actor, enforces actor constraints, and provides
CurrentActor. It adds user.roles to logs and spans from the verified active
organization membership, not the global user role. Events recorded in the handler
retain that role snapshot even after membership changes. Session-only router
middleware does not look up roles. When supplying AuthService directly without
AuthMiddleware, only the verified roles are added; session profile/actor
attribution still requires AuthMiddleware.
See the RBAC guide.
