effect-auth

Services and Primitives

Build custom authentication behavior from low-level services.

effect-auth models authentication capabilities as Effect services. A service tag such as PasswordLogin, Sessions, or Challenge is the typed name used to request a capability from an Effect environment. The tag describes what callers can do; it is not a global singleton and does not choose production infrastructure.

A Live layer constructs an implementation and declares its dependencies. For example, PasswordLoginLive implements PasswordLogin, while PasswordDefaultLive() assembles the password services that normally belong together. Layers let TypeScript report missing capabilities and let an application replace an implementation for a deployment or test without changing domain code.

Service families

The public surface is intentionally compositional rather than one giant auth object. Its major families are:

  • Flows and factors: AuthFlow, password, passkey, email OTP, magic link, TOTP, recovery code, and strong-factor services perform focused authentication transitions.
  • Sessions and challenges: Sessions, SessionCookie, and Challenge manage durable authentication state, browser tokens, and short-lived proof state.
  • Policy and security: AuthRateLimit, rate limiting, step-up, trusted-device, login-risk, permission, and guard services decide whether work may proceed.
  • Persistence and runtime: storage tags, crypto, secrets, configuration, mail, and WaitUntil isolate platform-specific effects.
  • Account and protocol features: OAuth, API keys, machine authentication, verification, audit, security timeline, retention, and webhooks build on those foundations.

Use a focused feature layer instead of manually collecting every implementation inside a family. Its required-service type is the current dependency contract.

Kernel composition

AuthKernelLive is the shared domain kernel, not a complete application runtime. Its current composition is:

storage + crypto + secrets + config (application layers)
                     |
                     v
 SessionsLive  SessionCookieLive  ChallengeLive  PrivacyLive
          \          |                |          /
           +------ AuthKernelLive ----------------+
                   |             |
               AuthFlow    AuthHttpTransport
                   |
                   v
       feature layers, such as PasswordDefaultLive()
                   |
                   v
             application boundary

The kernel provides its primitive layers to AuthFlowLive and AuthHttpTransportLive with Layer.provideMerge, so their outputs remain available to feature layers. The application must still provide every dependency visible in the resulting layer type. Composition follows dependencies, not initialization order.

Calling a primitive

A primitive is appropriate when authentication is one step in an application-specific workflow and an existing HTTP operation cannot express the required orchestration. The call itself remains small:

import { PasswordLogin } from "@effect-auth/core/Password";
import { Effect, Redacted } from "effect";

const authenticate = Effect.gen(function* () {
  const passwords = yield* PasswordLogin;
  return yield* passwords.signIn({
    identity: {
      scope: { type: "global" },
      kind: "email",
      value: "reader@example.com",
    },
    password: Redacted.make("correct horse battery staple"),
  });
});

This produces a typed domain effect. It is not an HTTP endpoint and does not inherit the controls supplied by HTTP operations or presets.

ConcernPrimitive ownsApplication owns
Domain behaviorCredential checks, invariants, typed domain failuresChoosing and sequencing services
SecuritySecurity internal to that capabilityOrigin/CSRF checks, authorization, rate limits, abuse policy, and auditing
ErrorsTyped domain error channelSanitization and mapping to declared public errors/statuses
Sessions and cookiesExplicit session operations when calledDeciding when to issue/rotate/revoke, committing every Set-Cookie, and clearing cookies
TransportNothingRequest decoding, response schemas, middleware, and client contract

Prefer Custom API when only routes, schemas, or middleware differ: HTTP operations retain standard security, error mapping, orchestration, and cookie commitment. Drop to primitives only when the workflow itself differs.

Composition is selective per endpoint. One server may mount a preset for passkeys, bind an HTTP operation for password sign-in, and use session primitives in a bespoke recovery flow. Supply only the feature layers those paths require; there is no need to enable the entire export surface.

See Architecture for integration levels, Sessions for token lifecycle, Security for operation policy, and Testing for replacing service implementations with deterministic layers.

On this page