---
title: "Browser Client"
url: "https://effect-auth.itsbroly.com/clients/browser-client/"
description: "Call typed authentication endpoints from browser applications."
---



`createAuthClient` is the browser-facing client for the standard effect-auth HTTP contract. It is exported from `@effect-auth/core/Client` and returns Promise-based operations suitable for UI code, query libraries, and framework actions.

The client and server contract are two views of the same schemas: `AuthClientProtocolApi` defines endpoint paths, payloads, successful responses, and public errors; the client validates inputs, calls those endpoints, and decodes responses. This catches contract drift instead of treating JSON as `unknown`. It does **not** discover application routes at runtime. See [Architecture](/concepts/architecture/) for where the HTTP contract sits relative to operations and primitives.

```text
Browser UI -> createAuthClient -> AuthClientProtocolApi contract -> auth HTTP server
              typed input       encoded request          session cookie
              decoded result <- decoded response <-------+
```

## Create a client [#create-a-client]

For an application that proxies `/auth/*` on its own origin, no configuration is required:

```ts
import { createAuthClient, isAuthApiError } from "@effect-auth/core/Client";

export const auth = createAuthClient();
```

`createAuthClient` accepts:

* `baseUrl`: an absolute server origin or URL base. Omit it for relative, same-origin requests.
* `requestInit`: defaults merged into every request. The built-in default is `{ credentials: "include" }`.
* `fetch`: a replacement `fetch`, useful for platform integration or tests.
* `browser.passkey`: browser credential overrides used by the high-level passkey methods.
* `protocol`: typed overrides, removals, and extensions described below.

The client owns an Effect managed runtime. Call `await auth.dispose()` when the client has a shorter lifetime than the page or application.

Effect applications can use `makeAuthHttpClient` instead. It returns the generated `AuthClientProtocolApi` client in an `Effect` requiring `HttpClient`, so the application owns transport, interruption, tracing, and lifecycle. This lower-level client uses contract-shaped calls such as `client.password.signIn({ payload })`; `createAuthClient` remains the recommended Promise facade for browser UI code and adds ergonomic workflows such as passkey orchestration.

```ts
import { makeAuthHttpClient } from "@effect-auth/core/Client";
import { Effect } from "effect";
import { FetchHttpClient } from "effect/unstable/http";

const program = Effect.gen(function* () {
  const client = yield* makeAuthHttpClient({
    baseUrl: "https://auth.example.com",
  });

  return yield* client.session.current();
}).pipe(Effect.provide(FetchHttpClient.layer));
```

## Cookies and origins [#cookies-and-origins]

Same-origin deployment is the simplest: leave `baseUrl` unset and serve or proxy the auth routes from the application's origin. Requests include cookies by default.

For a separate auth origin, set `baseUrl` and retain `credentials: "include"`. The server boundary must also return appropriate credentialed CORS headers, allow the browser application's origin, and issue cookies whose `SameSite`, `Secure`, domain, and path attributes permit the deployment. effect-auth's allowed-origin checks provide request-origin/CSRF protection; they do not configure CORS. Avoid changing `credentials` unless the endpoint intentionally does not use the browser session.

```ts
export const auth = createAuthClient({
  baseUrl: "https://auth.example.com",
  requestInit: { credentials: "include" },
});
```

## Method groups [#method-groups]

Methods follow the feature model rather than exposing raw URLs.

| Group                                        | Representative operations              |
| -------------------------------------------- | -------------------------------------- |
| `password`, `email`, `emailOtp`, `magicLink` | sign in/up, reset, start and verify    |
| `session`                                    | current, refresh, logout, list, revoke |
| `passkey`, `totp`, `recoveryCodes`           | enroll/register, verify, list, revoke  |
| `mfa`, `stepUp`, `loginApproval`             | continue or strengthen an auth flow    |
| `emailVerification`, `security`              | verify email, report a login           |

Feature-specific standalone clients are also exported from `@effect-auth/core/Client`, including `createIdentityClient`, `createPasskeyClient`, `createTotpClient`, and clients for OAuth, API keys, JWTs, trusted devices, security timelines, and administrative operations. `createAdminPermissionDefinitionClient` calls the separately mounted permission-definition lifecycle API and exposes `definitions.create/get/list/update/disable/enable/delete`; list accepts bounded keyset pagination with `after` and `limit`. The unified client exposes the same identity settings calls under `auth.identities`, but intentionally does not include permission administration. Use the unified client for normal browser auth; use a standalone client when you need that narrower contract.

## Results, failures, and cancellation [#results-failures-and-cancellation]

Authentication can succeed without being complete. Password, email OTP, and magic-link verification return a discriminated result whose `type` can be `authenticated`, `requires_mfa`, `requires_email_verification`, `requires_login_approval`, or `requires_passkey_enrollment`. These continuation values are successful responses, not exceptions. Preserve their `flowId` and other fields and route the user through the corresponding client group. The [Password guide](/authentication/password/#handle-authentication-continuations) shows exhaustive handling.

Failed HTTP responses reject the Promise with decoded public error objects. Narrow them with `isAuthApiError`, display a safe message with `authClientErrorMessage`, and obtain the mapped status with `authClientErrorStatus`. Schema/decode, network, and browser errors are not auth API errors, so keep an unknown fallback.

```ts
try {
  return await auth.password.signIn({
    identity: { scope: { type: "global" }, kind: "email", value: email },
    password,
  });
} catch (error) {
  if (isAuthApiError(error)) console.error(error.code);
  throw error;
}
```

Every asynchronous operation accepts `{ signal?: AbortSignal }` as its final argument. Cancellation interrupts the Effect execution and underlying fetch; this integrates directly with query libraries:

```ts
queryFn: ({ signal }) => auth.session.currentOrUndefined({ signal });
```

`currentOrUndefined` converts only the typed unauthenticated response to `undefined`; other failures still reject.

## Extensions and custom endpoints [#extensions-and-custom-endpoints]

`protocol` can replace a built-in operation, remove a group with `null`, or add local operations. For a custom HTTP endpoint, local functions alone provide no contract typing or response decoding. Define an Effect `HttpApi`, wrap it with `defineAuthHttpApiExtension`, and place that extension under `protocol.extensions`; the resulting operations share the client's base URL, fetch layer, request defaults, cancellation, and lifecycle.

Use overrides deliberately: replacing or removing standard operations means the browser contract no longer mirrors the standard `AuthClientProtocolApi`. Application-specific server endpoints always require a corresponding custom client extension (or a separately implemented client); `createAuthClient` cannot infer them.

Continue with the [Quick Start](/quick-start/#create-the-browser-client), [Architecture](/concepts/architecture/), or [Password authentication](/authentication/password/).

