Start
Getting started
Define a shared contract, bind it to authentication strategies, and supply your application’s services with Layers. The same contract gives your client typed Effects and atoms.
Install
Section titled “Install”bun add @yielded/auth@beta effectFor React, also install @effect/atom-react. Add companion packages for the
storage and protocol adapters you choose.
Define the shared contract
Section titled “Define the shared contract”import { Schema } from "effect";import { AuthContract } from "@yielded/auth";
export const AuthApi = AuthContract.make("app/Auth", { claims: Schema.Struct({ displayName: Schema.String }), actions: (sessions) => ({ signIn: AuthContract.passwordSignIn(sessions) }),});claims is the data each session carries. actions lists what clients may call;
here, password sign-in. Every contract also includes getSession,
requireSession, signOut, and renewSession. Keep this module free of server
configuration, keys, and persistence so the browser can import it.
Bind the server
Section titled “Bind the server”import { Auth, Http, Password, Sessions } from "@yielded/auth";
import { AuthApi } from "@app/domain/auth-contract";
export const AppAuth = Auth.make(AuthApi, { sessions: Sessions.stateful({ maxAge: "8 hours", idleTimeout: "30 minutes" }), strategies: { password: Password.make() }, defaultStrategy: "password",});
export const AuthRoutes = Http.layer(AppAuth, { origin: "https://app.example.com" });Auth.make declares a yieldable service; constructing it performs no I/O.
Http.layer serves the contract’s actions as routes and owns cookies, Origin,
and CSRF checks.
Supply storage and accounts
Section titled “Supply storage and accounts”The type of AuthRoutes lists every service your methods still need. The library
supplies Web Crypto and empty lifecycle hooks; you supply the rest:
| You supply | With |
|---|---|
| Storage for credentials, sessions, and proofs | Managed tables, your schema, or your services |
| Account checks and session claims | Your account Layers; see passwords |
| Password hashing | @yielded/auth-crypto/Password |
| Proof and request-binding keys | Your secrets; see Layer wiring |
Provide them to AuthRoutes and merge it with your router:
import { Layer } from "effect";
import { ApplicationRoutes } from "./application-routes";import { AuthRoutes } from "./auth";import { AuthDependencies } from "./auth-live"; // storage, accounts, hashing, keys
export const Routes = Layer.mergeAll(AuthRoutes, ApplicationRoutes).pipe( Layer.provide(AuthDependencies),);The managed Drizzle app
is a complete composition: schema.ts maps a customer table, live.ts supplies
accounts and storage, and server.ts serves the routes.
Call auth on the server
Section titled “Call auth on the server”Inside an Effect route covered by the auth middleware, call the service with your validated input:
const auth = yield* AppAuth;const result = yield* auth.signIn({ email, password });The request boundary supplies credentials and delivers the session cookie. An
Authenticated result carries the typed session; an additional-factor result must
be completed before you grant access. Failures stay typed in the error channel; see
rejected sign-ins.
Call it from the client
Section titled “Call it from the client”import { Atom as AuthAtom, Client } from "@yielded/auth";
import { AuthApi } from "@app/domain/auth-contract";
export const AppClient = Client.make(AuthApi, { baseUrl: "https://app.example.com" });export const auth = AuthAtom.make(AppClient);Use auth.session, auth.signIn, and auth.signOut directly as queries and
mutations with ordinary @effect/atom-react hooks. Compose your own queries
through auth.runtime to share the client and account lifetime:
import { Effect } from "effect";
import { AppClient, auth } from "./auth-client";
export const memberName = auth.runtime.atom( Effect.gen(function* () { const client = yield* AppClient; const session = yield* client.auth.getSession();
return session?.claims.displayName ?? null; }),);Fetch is configured by default. To customize transport, compose AppClient.layer
with your HttpClient Layer and pass { layer: ClientLive } to AuthAtom.make.
The Effect Atom client guide
shows React, shared invalidation, and standalone Effect calls.
Choose your next step
Section titled “Choose your next step”- Auth in an Effect application: services, Layers, identity, and ownership.
- Database and backend choices: keep managed tables, map your schema, or supply services.
- HTTP integration: mount routes and protect application handlers.
- Effect Atom client: render sessions, handle mutations, and compose workflows.
Then add an authentication method, such as passkeys or GitHub sign-in. Each method adds its required services to the server; its public actions belong in the shared contract.